- added form fields calculation and new table calculationl tested in preview;

This commit is contained in:
Vitalii Kiiko
2025-01-24 12:16:09 +01:00
parent e99a9b2058
commit df99a3e77d
17 changed files with 152 additions and 135 deletions

View File

@@ -59,11 +59,8 @@
&.table, &.criteria_table { &.table, &.criteria_table {
div.addNewTableRow { div.addNewTableRow {
width: 100%;
text-align: center; text-align: center;
padding: 5px 0; justify-content: center;
background: var(--table-border-color);
color: var(--primary-text);
&:hover { &:hover {
cursor: pointer; cursor: pointer;
@@ -99,14 +96,12 @@
min-width: 120px; min-width: 120px;
input { input {
width: 100%; width: 100%;
padding: 3px 5px;
} }
} }
tfoot td, tfoot td {
tfoot th { border-top: 1px solid var(--table-border-color);
border-top: 1px solid var(--table-border-color); 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) td,

View File

@@ -181,7 +181,7 @@
gap: 0.5rem; gap: 0.5rem;
} }
.formElementSettings__fieldDescription { .formElementSettings__fieldDescription, .formElementSettings__fieldVarsList {
padding: 15px; padding: 15px;
background-color: #ffe0c5; background-color: #ffe0c5;
border: 1px solid #e6a973; border: 1px solid #e6a973;
@@ -191,6 +191,7 @@
color: #c68e5e; color: #c68e5e;
font-size: 15px; font-size: 15px;
line-height: 1.5; line-height: 1.5;
}
code { code {
font-size: 14px; font-size: 14px;
@@ -198,8 +199,24 @@
border: 1px solid #e1b48b; border: 1px solid #e1b48b;
background-color: #fbeadb; background-color: #fbeadb;
border-radius: 3px; border-radius: 3px;
user-select: all;
} }
} }
.formElementSettings__fieldVarsList {
background-color: #e7fddd;
border: 1px solid #9de673;
p {
color: #5eae30;
}
code {
border: 1px solid #9de673;
background-color: #effaea;
margin-right: 4px;
user-select: all;
}
} }
.formElementSettings__tabs { .formElementSettings__tabs {

View File

@@ -1,24 +1,19 @@
import { InputText } from 'primereact/inputtext';
const DefaultCell = ({ getValue, row: { index }, column: { id }, table }) => { const DefaultCell = ({ getValue, row: { index }, column: { id }, table }) => {
const initialValue = getValue(); const initialValue = getValue();
const disabled = table.options.meta?.disabled; const disabled = table.options.meta?.disabled;
const onBlur = (e) => {
table.options.meta?.updateData(index, id, e.target.value);
};
const onFocus = (e) => { const onFocus = (e) => {
e.target.select(); e.target.select();
} }
return ( return (
<input <InputText
disabled={disabled}
value={initialValue ?? ''} value={initialValue ?? ''}
onChange={(e) => table.options.meta?.updateData(index, id, e.target.value)} disabled={disabled}
onBlur={onBlur}
onFocus={onFocus} onFocus={onFocus}
className="w-full px-2 py-1 border rounded" onChange={(e) => table.options.meta?.updateData(index, id, e.target.value)} />
/>
); );
}; };

View File

@@ -1,32 +1,28 @@
import { InputNumber } from 'primereact/inputnumber';
const NumericFormulaCell = ({ getValue, row: { index }, column: { id }, table }) => { const NumericFormulaCell = ({ getValue, row: { index }, column: { id }, table }) => {
const initialValue = getValue(); const initialValue = getValue();
const disabled = table.options.meta?.disabled; 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 onFocus = (e) => { const onFocus = (e) => {
e.target.select(); e.target.select();
} }
const onChange = (e) => { const onChange = (value) => {
if (e.target.value === 0 || !isNaN(e.target.value)) { table.options.meta?.updateData(index, id, value);
table.options.meta?.updateData(index, id, e.target.value);
}
}; };
return ( return (
<input <InputNumber
type="number"
disabled={disabled} disabled={disabled}
value={initialValue ?? 0} value={initialValue ?? 0}
onChange={onChange} onValueChange={(e) => onChange(e.value)}
onFocus={onFocus} onFocus={onFocus}
onBlur={onBlur} minFractionDigits={0}
step="any" maxFractionDigits={2}
className="w-full px-2 py-1 border rounded" locale='it-IT'
useGrouping={false}
showButtons
/> />
); );
}; };

View File

@@ -82,17 +82,17 @@ const RenderTable = ({ tableValue = {}, columnsCfg, lastRowCfg, setTableValueFn,
</tr> </tr>
); );
})} })}
</tbody>
{!isEmpty(lastRowCfg) {!isEmpty(lastRowCfg)
? <tr> ? <tfoot><tr>
{columnsCfg.map((o) => <LastRowCell {columnsCfg.map((o) => <LastRowCell
key={o.accessorKey} key={o.accessorKey}
columnId={o.accessorKey} columnId={o.accessorKey}
columnMeta={o.meta} columnMeta={o.meta}
lastRowCfg={lastRowCfg} lastRowCfg={lastRowCfg}
tableValue={tableValue}/>)} tableValue={tableValue}/>)}
</tr> </tr></tfoot>
: null} : null}
</tbody>
</table> </table>
) )
} }

View File

@@ -58,10 +58,10 @@ const Table = ({
} }
} }
if (o.predefined) { if (o.enableFormula || o.fieldtype === 'numeric') {
item.cell = (info) => info.getValue();
} else if (o.enableFormula || o.fieldtype === 'numeric') {
item.cell = NumericFormulaCell; item.cell = NumericFormulaCell;
} else {
item.cell = (info) => info.getValue();
} }
return item; return item;

View File

@@ -1,24 +1,19 @@
import { InputText } from 'primereact/inputtext';
const DefaultCell = ({ getValue, row: { index }, column: { id }, table }) => { const DefaultCell = ({ getValue, row: { index }, column: { id }, table }) => {
const initialValue = getValue(); const initialValue = getValue();
const disabled = table.options.meta?.disabled; const disabled = table.options.meta?.disabled;
const onBlur = (e) => {
table.options.meta?.updateData(index, id, e.target.value);
};
const onFocus = (e) => { const onFocus = (e) => {
e.target.select(); e.target.select();
} }
return ( return (
<input <InputText
disabled={disabled}
value={initialValue ?? ''} value={initialValue ?? ''}
onChange={(e) => table.options.meta?.updateData(index, id, e.target.value)} disabled={disabled}
onBlur={onBlur}
onFocus={onFocus} onFocus={onFocus}
className="w-full px-2 py-1 border rounded" onChange={(e) => table.options.meta?.updateData(index, id, e.target.value)} />
/>
); );
}; };

View File

@@ -1,32 +1,28 @@
import { InputNumber } from 'primereact/inputnumber';
const NumericFormulaCell = ({ getValue, row: { index }, column: { id }, table }) => { const NumericFormulaCell = ({ getValue, row: { index }, column: { id }, table }) => {
const initialValue = getValue(); const initialValue = getValue();
const disabled = table.options.meta?.disabled; 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 onFocus = (e) => { const onFocus = (e) => {
e.target.select(); e.target.select();
} }
const onChange = (e) => { const onChange = (value) => {
if (e.target.value === 0 || !isNaN(e.target.value)) { table.options.meta?.updateData(index, id, value);
table.options.meta?.updateData(index, id, e.target.value);
}
}; };
return ( return (
<input <InputNumber
type="number"
disabled={disabled} disabled={disabled}
value={initialValue ?? 0} value={initialValue ?? 0}
onChange={onChange} onValueChange={(e) => onChange(e.value)}
onFocus={onFocus} onFocus={onFocus}
onBlur={onBlur} minFractionDigits={0}
step="any" maxFractionDigits={2}
className="w-full px-2 py-1 border rounded" locale='it-IT'
useGrouping={false}
showButtons
/> />
); );
}; };

View File

@@ -68,17 +68,17 @@ const RenderTable = ({ rowsData, columnsCfg, lastRowCfg, setRowsFn, disabled })
</tr> </tr>
); );
})} })}
</tbody>
{!isEmpty(lastRowCfg) {!isEmpty(lastRowCfg)
? <tr> ? <tfoot><tr>
{columnsCfg.map((o) => <LastRowCell {columnsCfg.map((o) => <LastRowCell
key={o.accessorKey} key={o.accessorKey}
columnId={o.accessorKey} columnId={o.accessorKey}
columnMeta={o.meta} columnMeta={o.meta}
lastRowCfg={lastRowCfg} lastRowCfg={lastRowCfg}
getColumnDataFn={getColumnData}/>)} getColumnDataFn={getColumnData}/>)}
</tr> </tr></tfoot>
: null} : null}
</tbody>
</table> </table>
) )
} }

View File

@@ -176,7 +176,7 @@ const Table = ({
setRowsFn={updateRows} setRowsFn={updateRows}
disabled={disabled}/> : null} disabled={disabled}/> : null}
{!isEmpty(columns) && !shouldDisableNewRows {!isEmpty(columns) && !shouldDisableNewRows
? <div className="addNewTableRow" onClick={addNewRow}> ? <div className="addNewTableRow p-button p-component" onClick={addNewRow}>
{__('Aggiungi una riga', 'gepafin')} {__('Aggiungi una riga', 'gepafin')}
</div> </div>
: null} : null}

3
src/helpers/keepKeys.js Normal file
View File

@@ -0,0 +1,3 @@
const keepKeys = (arr, keys) => arr.map(obj => Object.fromEntries(keys.map(k => [k, obj[k]])));
export default keepKeys;

3
src/helpers/removeKey.js Normal file
View File

@@ -0,0 +1,3 @@
const removeKey = (arr, key) => arr.map(({[key]: _, ...rest}) => rest);
export default removeKey;

View File

@@ -1,6 +1,9 @@
import React from 'react'; import React, { useEffect, useState } from 'react';
import { __ } from '@wordpress/i18n'; import { __ } from '@wordpress/i18n';
import { is } from 'ramda'; import { head, is, isEmpty, isNil, uniq } from 'ramda';
// store
import { storeGet } from '../../../../../../store';
// tools // tools
import renderHtmlContent from '../../../../../../helpers/renderHtmlContent'; import renderHtmlContent from '../../../../../../helpers/renderHtmlContent';
@@ -19,6 +22,8 @@ import { mimeTypes } from '../../../../../../configData';
const ElementSetting = ({ setting, changeFn, updateDataFn, bandoStatus }) => { const ElementSetting = ({ setting, changeFn, updateDataFn, bandoStatus }) => {
const [existingVars, setExistingVars] = useState([]);
const settingLabels = { const settingLabels = {
label: __('Label', 'gepafin'), label: __('Label', 'gepafin'),
placeholder: __('Segnaposto', 'gepafin'), placeholder: __('Segnaposto', 'gepafin'),
@@ -115,9 +120,30 @@ const ElementSetting = ({ setting, changeFn, updateDataFn, bandoStatus }) => {
} }
} }
useEffect(() => {
const elements = storeGet.main.formElements();
const activeElement = storeGet.main.activeElement();
const vars = elements
.filter(o => o.id !== activeElement)
// eslint-disable-next-line
.map((o) => {
const variableSetting = head(o.settings.filter(s => s.name === 'variable'));
if (variableSetting) {
return variableSetting.value[0];
}
})
.filter(v => !isNil(v));
setExistingVars(uniq(vars));
}, []);
return <div className="formElementSettings__field" key={setting.name}> return <div className="formElementSettings__field" key={setting.name}>
<label htmlFor={setting.name}>{settingLabels[setting.name]}</label> <label htmlFor={setting.name}>{settingLabels[setting.name]}</label>
{getProperField(setting)} {getProperField(setting)}
{setting.name === 'formula' && !isEmpty(existingVars)
? <div className="formElementSettings__fieldVarsList">
<p>Existing variables: {existingVars.map(v => <code key={v}>{`{${v}}`}</code>)}</p>
</div> : null}
{settingDescription[setting.name] {settingDescription[setting.name]
? <div className="formElementSettings__fieldDescription"> ? <div className="formElementSettings__fieldDescription">
<p>{renderHtmlContent(settingDescription[setting.name])}</p> <p>{renderHtmlContent(settingDescription[setting.name])}</p>

View File

@@ -1,17 +1,18 @@
import React, { useCallback, 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, last, pathOr } from 'ramda';
// components // components
import { InputText } from 'primereact/inputtext'; import { InputText } from 'primereact/inputtext';
import { Button } from 'primereact/button'; import { Button } from 'primereact/button';
import { InputSwitch } from 'primereact/inputswitch'; import { InputSwitch } from 'primereact/inputswitch';
import { Dropdown } from 'primereact/dropdown';
import { Accordion, AccordionTab } from 'primereact/accordion';
// tools // tools
import uniqid from '../../../../../../helpers/uniqid'; import uniqid from '../../../../../../helpers/uniqid';
import { Dropdown } from 'primereact/dropdown'; import removeKey from '../../../../../../helpers/removeKey';
import { Accordion, AccordionTab } from 'primereact/accordion';
const ElementSettingCriteriaTableColumns = ({ const ElementSettingCriteriaTableColumns = ({
value, value,
@@ -24,13 +25,15 @@ const ElementSettingCriteriaTableColumns = ({
const removeItem = (index) => { const removeItem = (index) => {
let newData = stateFieldData let newData = stateFieldData
.toSpliced(index, 1) .toSpliced(index, 1);
newData = newData.map((o, i) => { newData = newData.map((o, i) => {
return i === newData.length - 1 return i === newData.length - 1
? {...o, fieldtype: 'numeric', predefined: false, enableFormula: true} ? {...o, fieldtype: 'numeric', predefined: false, enableFormula: true}
: {...o, fieldtype: 'text', predefined: true, enableFormula: false} : {...o, fieldtype: 'text', predefined: true, enableFormula: false}
}); });
setStateFieldData(newData); setStateFieldData(newData);
const newRowsData = removeKey(rowsData, last(newData.map(o => o.name)));
setRowsData(newRowsData);
} }
const addNewItem = () => { const addNewItem = () => {

View File

@@ -1,17 +1,18 @@
import React, { useCallback, 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, last, pathOr } from 'ramda';
// components // components
import { InputText } from 'primereact/inputtext'; import { InputText } from 'primereact/inputtext';
import { Button } from 'primereact/button'; import { Button } from 'primereact/button';
import { InputSwitch } from 'primereact/inputswitch'; import { InputSwitch } from 'primereact/inputswitch';
import { Dropdown } from 'primereact/dropdown';
import { Accordion, AccordionTab } from 'primereact/accordion';
// tools // tools
import uniqid from '../../../../../../helpers/uniqid'; import uniqid from '../../../../../../helpers/uniqid';
import { Dropdown } from 'primereact/dropdown'; import removeKey from '../../../../../../helpers/removeKey';
import { Accordion, AccordionTab } from 'primereact/accordion';
const ElementSettingTableColumns = ({ const ElementSettingTableColumns = ({
value, value,
@@ -25,6 +26,8 @@ const ElementSettingTableColumns = ({
const removeItem = (index) => { const removeItem = (index) => {
const newData = stateFieldData.toSpliced(index, 1); const newData = stateFieldData.toSpliced(index, 1);
setStateFieldData(newData); setStateFieldData(newData);
const newRowsData = removeKey(rowsData, last(newData.map(o => o.name)));
setRowsData(newRowsData);
} }
const addNewItem = () => { const addNewItem = () => {

View File

@@ -2,7 +2,7 @@ import React, { useState, useEffect } from 'react';
import { __ } from '@wordpress/i18n'; import { __ } from '@wordpress/i18n';
import { useNavigate, useParams } from 'react-router-dom'; import { useNavigate, useParams } from 'react-router-dom';
import { klona } from 'klona'; import { klona } from 'klona';
import { head, isNil , isEmpty } from 'ramda'; import { head, isNil, isEmpty, pathOr } from 'ramda';
import { useForm } from 'react-hook-form'; import { useForm } from 'react-hook-form';
import 'quill/dist/quill.core.css'; import 'quill/dist/quill.core.css';
import { evaluate } from 'mathjs'; import { evaluate } from 'mathjs';
@@ -84,23 +84,6 @@ const BandoFormsPreview = () => {
if (data.status === 'SUCCESS') { if (data.status === 'SUCCESS') {
setFormName(data.data.label); setFormName(data.data.label);
const elements = klona(data.data.content); const elements = klona(data.data.content);
let allvars = {};
let allformulas = {};
// eslint-disable-next-line array-callback-return
elements.map((o) => {
const variable = head(o.settings.filter(o => o.name === 'variable'));
if (variable && !isEmpty(variable.value)) {
allvars[o.id] = variable.value[0];
}
const formula = head(o.settings.filter(o => o.name === 'formula'));
if (formula && !isEmpty(formula.value)) {
allformulas[o.id] = formula.value;
}
});
setFieldsWithVars(allvars);
setFieldsWithFormula(allformulas);
setFormData(elements); setFormData(elements);
} }
storeSet.main.unsetAsyncRequest(); storeSet.main.unsetAsyncRequest();
@@ -112,38 +95,40 @@ const BandoFormsPreview = () => {
} }
useEffect(() => { useEffect(() => {
if (!isEmpty(fieldsWithVars) && !isEmpty(fieldsWithFormula)) { let updatedFormValues = klona(formValues);
const updatedFormValues = klona(formValues);
let context = {}; let context = {};
// eslint-disable-next-line array-callback-return // eslint-disable-next-line array-callback-return
Object.keys(updatedFormValues).map(fieldId => { formData.map((o) => {
if (!isNil(fieldsWithFormula[fieldId])) { const variable = head(o.settings.filter(o => o.name === 'variable'));
const formula = fieldsWithFormula[fieldId]; const formula = head(o.settings.filter(o => o.name === 'formula'));
context = getTokens(formula)
if (formula && !isEmpty(formula.value)) {
context = getTokens(formula.value)
.filter(v => !['false', 'null', 'true'].includes(v)) .filter(v => !['false', 'null', 'true'].includes(v))
.reduce((acc, cur) => { .reduce((acc, cur) => {
acc[cur] = isNil(context[cur]) ? 0 : context[cur]; acc[cur] = isNil(context[cur]) ? 0 : context[cur];
return acc; return acc;
}, {}); }, context);
const mathFormula = renderWithDataVars(formula, context); const mathFormula = renderWithDataVars(formula.value, context);
try { try {
updatedFormValues[fieldId] = evaluate(mathFormula); updatedFormValues[o.id] = evaluate(mathFormula);
} catch (e) { } catch (e) {
console.log('Error in math formula: "', mathFormula, '"', e.message); console.log('Error in math formula: "', mathFormula, '"', e.message);
updatedFormValues[fieldId] = 0; updatedFormValues[o.id] = 0;
} }
} }
if (!isNil(fieldsWithVars[fieldId])) {
context[fieldsWithVars[fieldId]] = updatedFormValues[fieldId] if (variable && !isEmpty(variable.value)) {
context[variable.value[0]] = 'criteria_table' === o.name
? pathOr(0, [o.id, 'total'], updatedFormValues)
: pathOr(0, [o.id], updatedFormValues);
} }
}); });
if (!isEmpty(updatedFormValues) && !equal(updatedFormValues, formValues)) { if (!isEmpty(updatedFormValues) && !equal(updatedFormValues, formValues)) {
reset(updatedFormValues); reset(updatedFormValues);
} }
}
}, [formValues]); }, [formValues]);
useEffect(() => { useEffect(() => {

View File

@@ -1,8 +1,8 @@
import React, { useRef, useState, useEffect, useMemo } from 'react'; import React, { useRef, useState, useEffect } from 'react';
import { __, sprintf } from '@wordpress/i18n'; import { __, sprintf } from '@wordpress/i18n';
import { useForm } from 'react-hook-form'; import { useForm } from 'react-hook-form';
import { classNames } from 'primereact/utils'; import { classNames } from 'primereact/utils';
import { isEmpty, isNil } from 'ramda'; import { isEmpty } from 'ramda';
import { useNavigate, useSearchParams } from 'react-router-dom'; import { useNavigate, useSearchParams } from 'react-router-dom';
// tools // tools