- saving progress;
This commit is contained in:
@@ -0,0 +1,25 @@
|
||||
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);
|
||||
};
|
||||
|
||||
const onFocus = (e) => {
|
||||
e.target.select();
|
||||
}
|
||||
|
||||
return (
|
||||
<input
|
||||
disabled={disabled}
|
||||
value={initialValue ?? ''}
|
||||
onChange={(e) => table.options.meta?.updateData(index, id, e.target.value)}
|
||||
onBlur={onBlur}
|
||||
onFocus={onFocus}
|
||||
className="w-full px-2 py-1 border rounded"
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default DefaultCell;
|
||||
@@ -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) || isNil(v) ? 0 : v);
|
||||
|
||||
if (cellValue === 'sum') {
|
||||
cellValue = sum(getAllRowsValues);
|
||||
} else {
|
||||
cellValue = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return <td>{cellValue}</td>;
|
||||
};
|
||||
|
||||
export default LastRowCell;
|
||||
@@ -0,0 +1,34 @@
|
||||
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 onFocus = (e) => {
|
||||
e.target.select();
|
||||
}
|
||||
|
||||
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}
|
||||
onFocus={onFocus}
|
||||
onBlur={onBlur}
|
||||
step="any"
|
||||
className="w-full px-2 py-1 border rounded"
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default NumericFormulaCell;
|
||||
@@ -0,0 +1,86 @@
|
||||
import React from 'react';
|
||||
import { flexRender, getCoreRowModel, useReactTable } from '@tanstack/react-table';
|
||||
import { wrap } from 'object-path-immutable';
|
||||
import { isEmpty } from 'ramda';
|
||||
|
||||
// components
|
||||
import DefaultCell from './components/DefaultCell';
|
||||
import LastRowCell from './components/LastRowCell';
|
||||
|
||||
const RenderTable = ({ data, columns, lastRow, setRowsFn, disabled }) => {
|
||||
const table = useReactTable({
|
||||
data,
|
||||
columns,
|
||||
defaultColumn: {
|
||||
cell: DefaultCell
|
||||
},
|
||||
getCoreRowModel: getCoreRowModel(),
|
||||
meta: {
|
||||
disabled,
|
||||
updateData: (rowIndex, columnId, value) => {
|
||||
const newRowsData = wrap(data).set([rowIndex, columnId], value).value();
|
||||
setRowsFn(newRowsData);
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
const getColumnData = (columnId) => {
|
||||
const rows = table.getRowModel().rows;
|
||||
return rows.map(row => row.getValue(columnId));
|
||||
};
|
||||
|
||||
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>
|
||||
);
|
||||
})}
|
||||
{!isEmpty(lastRow)
|
||||
? <tr>
|
||||
{columns.map((o) => <LastRowCell
|
||||
key={o.accessorKey}
|
||||
columnId={o.accessorKey}
|
||||
columnMeta={o.meta}
|
||||
lastRows={lastRow}
|
||||
getColumnDataFn={getColumnData}/>)}
|
||||
</tr>
|
||||
: null}
|
||||
</tbody>
|
||||
</table>
|
||||
)
|
||||
}
|
||||
|
||||
export default RenderTable
|
||||
132
src/components/FormField/components/CriteriaTable/index.js
Normal file
132
src/components/FormField/components/CriteriaTable/index.js
Normal file
@@ -0,0 +1,132 @@
|
||||
import React, { useEffect, useState, useCallback } from 'react';
|
||||
import { classNames } from 'primereact/utils';
|
||||
import { pathOr, isEmpty } from 'ramda';
|
||||
import { wrap } from 'object-path-immutable';
|
||||
import equal from 'fast-deep-equal';
|
||||
import { klona } from 'klona';
|
||||
|
||||
// tools
|
||||
import { nonEmptyTables } from '../../../../helpers/validators';
|
||||
|
||||
//components
|
||||
import RenderTable from './RenderTable';
|
||||
import NumericFormulaCell from './RenderTable/components/NumericFormulaCell';
|
||||
|
||||
const Table = ({
|
||||
fieldName,
|
||||
setDataFn,
|
||||
label,
|
||||
register,
|
||||
errors,
|
||||
disabled = false,
|
||||
config = {},
|
||||
defaultValue = [],
|
||||
tableColumns = []
|
||||
}) => {
|
||||
const [columnsCfg, setColumnsCfg] = useState([]);
|
||||
const [rowsCfg, setRowsCfg] = useState([]);
|
||||
const [columns, setColumns] = useState([]);
|
||||
const [lastRow, setLastRow] = useState([]);
|
||||
const [rows, setRows] = useState(null);
|
||||
|
||||
const updateRows = useCallback((data) => {
|
||||
setRows(data);
|
||||
setDataFn(fieldName, data, { shouldValidate: true });
|
||||
}, [rows, defaultValue]);
|
||||
|
||||
const properConfig = (config) => {
|
||||
let newConfig = klona(config);
|
||||
if (config.validate && config.validate.nonEmptyTables) {
|
||||
newConfig = wrap(newConfig)
|
||||
.set(['validate', 'nonEmptyTables'], (v) => nonEmptyTables(v, tableColumns))
|
||||
.value();
|
||||
}
|
||||
|
||||
return newConfig;
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
let newColumns = columnsCfg.map((o) => {
|
||||
const item = {
|
||||
accessorKey: o.name,
|
||||
header: () => o.label,
|
||||
footer: (props) => props.column.id,
|
||||
meta: {
|
||||
predefined: o.predefined,
|
||||
enableFormula: o.enableFormula,
|
||||
fieldtype: o.fieldtype
|
||||
}
|
||||
}
|
||||
|
||||
if (o.predefined) {
|
||||
item.cell = (info) => info.getValue();
|
||||
} else if (o.enableFormula || o.fieldtype === 'numeric') {
|
||||
item.cell = NumericFormulaCell;
|
||||
}
|
||||
|
||||
return item;
|
||||
});
|
||||
|
||||
setColumns(newColumns);
|
||||
}, [columnsCfg, disabled]);
|
||||
|
||||
useEffect(() => {
|
||||
setRows(rowsCfg);
|
||||
}, [rowsCfg]);
|
||||
|
||||
useEffect(() => {
|
||||
const stateFieldData = pathOr([], ['stateFieldData'], tableColumns);
|
||||
const obj = stateFieldData
|
||||
.reduce((acc, cur) => {
|
||||
acc[cur.name] = ''
|
||||
return acc;
|
||||
}, {});
|
||||
let rowsData = pathOr([obj], ['rowsData'], tableColumns);
|
||||
rowsData = isEmpty(rowsData) ? [obj] : rowsData;
|
||||
setColumnsCfg(stateFieldData);
|
||||
setRowsCfg(rowsData);
|
||||
|
||||
let lastRowData = stateFieldData.reduce((acc, cur) => {
|
||||
const value = cur.enableFormula ? cur.lastRowFormula : (cur.lastRowText ? cur.lastRowText : '');
|
||||
acc.push({ [cur.name]: value });
|
||||
return acc;
|
||||
}, []);
|
||||
|
||||
const lastRowValues = lastRowData
|
||||
.map(o => {
|
||||
const values = Object.values(o);
|
||||
return values[0];
|
||||
})
|
||||
.filter(v => !isEmpty(v));
|
||||
|
||||
setLastRow(!isEmpty(lastRowValues) ? lastRowData : []);
|
||||
}, [tableColumns]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!equal(rows, defaultValue)) {
|
||||
setRows(defaultValue);
|
||||
}
|
||||
}, [defaultValue]);
|
||||
|
||||
useEffect(() => {
|
||||
setRows(defaultValue);
|
||||
register(fieldName, properConfig(config));
|
||||
}, []);
|
||||
|
||||
console.log('rows', rows, lastRow, tableColumns)
|
||||
return (
|
||||
<>
|
||||
<label htmlFor={fieldName} className={classNames({ 'p-error': errors[fieldName] })}>
|
||||
{label}{config.required || config.isRequired || (config.validate && config.validate.nonEmptyTables)
|
||||
? <span className="appForm__field--required">*</span> : null}
|
||||
</label>
|
||||
{rows ? <RenderTable
|
||||
columns={columns}
|
||||
data={rows}
|
||||
lastRow={lastRow}
|
||||
setRowsFn={updateRows}
|
||||
disabled={disabled}/> : null}
|
||||
</>)
|
||||
}
|
||||
|
||||
export default Table;
|
||||
@@ -21,6 +21,7 @@ const NumberInput = ({
|
||||
min,
|
||||
max,
|
||||
disabled = false,
|
||||
readOnly = false,
|
||||
useGrouping = true
|
||||
}) => {
|
||||
const minAttr = config.min ? config.min : min;
|
||||
@@ -33,11 +34,13 @@ const NumberInput = ({
|
||||
render={({ field, fieldState }) => (
|
||||
<InputNumber inputId={field.name}
|
||||
disabled={disabled}
|
||||
readOnly={readOnly}
|
||||
value={field.value}
|
||||
onValueChange={(e) => field.onChange(e.value)}
|
||||
min={minAttr}
|
||||
max={maxAttr}
|
||||
locale={locale}
|
||||
showButtons
|
||||
useGrouping={useGrouping}
|
||||
maxFractionDigits={!isNaN(parseInt(maxFractionDigits)) ? parseInt(maxFractionDigits) : 0}
|
||||
minFractionDigits={!isNaN(parseInt(minFractionDigits)) ? parseInt(minFractionDigits) : 0}
|
||||
|
||||
@@ -17,6 +17,7 @@ import Checkboxes from './components/Checkboxes';
|
||||
import Fileupload from './components/Fileupload';
|
||||
import Table from './components/Table';
|
||||
import PasswordField from './components/PasswordField';
|
||||
import CriteriaTable from './components/CriteriaTable';
|
||||
|
||||
const FormField = (props) => {
|
||||
const fields = {
|
||||
@@ -33,6 +34,7 @@ const FormField = (props) => {
|
||||
wysiwyg: Wysiwyg,
|
||||
checkboxes: Checkboxes,
|
||||
table: Table,
|
||||
criteria_table: CriteriaTable,
|
||||
password: PasswordField
|
||||
}
|
||||
const Comp = !isNil(fields[props.type]) ? fields[props.type] : null;
|
||||
|
||||
Reference in New Issue
Block a user