diff --git a/package.json b/package.json index ccd2660..7edb643 100644 --- a/package.json +++ b/package.json @@ -4,38 +4,41 @@ "private": true, "dependencies": { "@babel/plugin-proposal-private-property-in-object": "7.21.11", - "@babel/preset-react": "7.25.9", + "@babel/preset-react": "7.26.3", "@date-fns/tz": "1.2.0", "@emailjs/browser": "4.4.1", - "@emotion/styled": "11.13.5", - "@number-flow/react": "0.4.2", - "@sentry/browser": "8.42.0", + "@emotion/styled": "11.14.0", + "@number-flow/react": "0.5.5", + "@sentry/browser": "8.51.0", "@stomp/stompjs": "7.0.0", - "@tanstack/react-table": "8.20.5", - "@wordpress/i18n": "5.13.0", - "@wordpress/react-i18n": "4.13.0", + "@tanstack/react-table": "8.20.6", + "@wordpress/i18n": "5.16.0", + "@wordpress/react-i18n": "4.16.0", "codice-fiscale-js": "2.3.22", "copy-to-clipboard": "3.3.3", "deep-object-diff": "1.1.9", - "dompurify": "3.2.2", + "dompurify": "3.2.3", + "expression-language": "^1.2.0", "fast-deep-equal": "3.1.3", - "hotkeys-js": "3.13.7", - "html-react-parser": "5.1.18", + "hotkeys-js": "3.13.9", + "html-react-parser": "5.2.2", "jwt-decode": "4.0.0", "klona": "2.0.6", "leader-line-new": "1.1.9", "luxon": "3.5.0", + "mathjs": "^14.0.1", + "mustache": "^4.2.0", "object-path-immutable": "4.1.2", "primeicons": "7.0.0", - "primereact": "10.8.5", + "primereact": "10.9.2", "quill": "2.0.3", "ramda": "0.30.1", "react": "18.3.1", "react-dnd": "16.0.1", "react-dnd-html5-backend": "16.0.1", "react-dom": "18.3.1", - "react-hook-form": "7.53.2", - "react-router-dom": "7.0.1", + "react-hook-form": "7.54.2", + "react-router-dom": "7.1.3", "react-scripts": "5.0.1", "recharts": "2.15.0", "sockjs-client": "^1.6.1", @@ -44,14 +47,14 @@ "zustand-x": "3.0.4" }, "devDependencies": { - "@babel/cli": "7.25.9", - "@babel/core": "7.26.0", + "@babel/cli": "7.26.4", + "@babel/core": "7.26.7", "@babel/plugin-syntax-jsx": "7.25.9", - "@wordpress/babel-plugin-makepot": "6.13.0", + "@wordpress/babel-plugin-makepot": "6.16.0", "babel-plugin-macros": "3.1.0", "node-wp-i18n": "1.2.7", - "sass": "1.81.0", - "sass-loader": "16.0.3" + "sass": "1.83.4", + "sass-loader": "16.0.4" }, "scripts": { "start": "GENERATE_SOURCEMAP=false react-scripts start", diff --git a/src/assets/scss/components/appForm.scss b/src/assets/scss/components/appForm.scss index bb532bd..dad056e 100644 --- a/src/assets/scss/components/appForm.scss +++ b/src/assets/scss/components/appForm.scss @@ -57,13 +57,10 @@ background-color: #e3e3e3; } - &.table { + &.table, &.criteria_table { div.addNewTableRow { - width: 100%; text-align: center; - padding: 5px 0; - background: var(--table-border-color); - color: var(--primary-text); + justify-content: center; &:hover { cursor: pointer; @@ -88,7 +85,6 @@ color: var(--global-textColor); font-size: 15px; text-align: left; - text-align: start; } th { @@ -97,16 +93,15 @@ } td { + min-width: 120px; input { width: 100%; - padding: 3px 5px; } } - tfoot td, - tfoot th { + tfoot td { + 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, diff --git a/src/assets/scss/components/formBuilder.scss b/src/assets/scss/components/formBuilder.scss index b07f322..db9b8ad 100644 --- a/src/assets/scss/components/formBuilder.scss +++ b/src/assets/scss/components/formBuilder.scss @@ -58,6 +58,11 @@ flex-direction: column; gap: 5px; align-items: flex-start; + + .tagHeader { + display: flex; + gap: 10px; + } } .label { @@ -176,6 +181,44 @@ gap: 0.5rem; } +.formElementSettings__fieldDescription, .formElementSettings__fieldVarsList { + padding: 15px; + background-color: #ffe0c5; + border: 1px solid #e6a973; + + p { + margin: 0; + color: #c68e5e; + font-size: 15px; + line-height: 1.5; + } + + code { + font-size: 14px; + padding: 3px 6px; + border: 1px solid #e1b48b; + background-color: #fbeadb; + 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 { width: 100%; diff --git a/src/assets/scss/components/layout.scss b/src/assets/scss/components/layout.scss index e350503..5b617be 100644 --- a/src/assets/scss/components/layout.scss +++ b/src/assets/scss/components/layout.scss @@ -116,6 +116,20 @@ img { } } } + + li div.nonLink { + display: flex; + padding: 10.5px 17.5px; + align-items: center; + font-size: 14px; + font-style: normal; + font-weight: 400; + line-height: 100%; + text-decoration: none; + border-bottom: 1px solid var(--menu-borderColor); + background-color: var(--button-secondary-borderColor); + color: white; + } } } diff --git a/src/assets/scss/components/misc.scss b/src/assets/scss/components/misc.scss index 8f89d4f..309e5ff 100644 --- a/src/assets/scss/components/misc.scss +++ b/src/assets/scss/components/misc.scss @@ -31,6 +31,10 @@ } } .p-tag { + &.p-tag-secondary { + background-color: var(--table-border-color); + } + .p-tag-value { color: var(--menuitem-active-color); } @@ -147,7 +151,7 @@ max-width: 100%; } -.p-dropdown { +.p-dropdown, .p-chips, .p-chips-multiple-container { width: 100%; } @@ -159,6 +163,10 @@ } } +.p-inputnumber-input[readonly] { + background-color: #e1e1e1; +} + .p-inputgroup.flex-1 { align-items: center; } diff --git a/src/components/FormField/components/CriteriaTable/RenderTable/components/DefaultCell/index.js b/src/components/FormField/components/CriteriaTable/RenderTable/components/DefaultCell/index.js new file mode 100644 index 0000000..0df7324 --- /dev/null +++ b/src/components/FormField/components/CriteriaTable/RenderTable/components/DefaultCell/index.js @@ -0,0 +1,20 @@ +import { InputText } from 'primereact/inputtext'; + +const DefaultCell = ({ getValue, row: { index }, column: { id }, table }) => { + const initialValue = getValue(); + const disabled = table.options.meta?.disabled; + + const onFocus = (e) => { + e.target.select(); + } + + return ( + table.options.meta?.updateData(index, id, e.target.value)} /> + ); +}; + +export default DefaultCell; \ No newline at end of file diff --git a/src/components/FormField/components/CriteriaTable/RenderTable/components/LastRowCell/index.js b/src/components/FormField/components/CriteriaTable/RenderTable/components/LastRowCell/index.js new file mode 100644 index 0000000..c9e12a0 --- /dev/null +++ b/src/components/FormField/components/CriteriaTable/RenderTable/components/LastRowCell/index.js @@ -0,0 +1,14 @@ +import { head, isNil, pathOr } from 'ramda'; + +const LastRowCell = ({columnId, lastRowCfg, columnMeta = {}, tableValue = []}) => { + const cellData = head(lastRowCfg.filter(o => !isNil(o[columnId]))); + let cellValue = cellData[columnId]; + + if (columnMeta.enableFormula) { + cellValue = pathOr(0, ['total'], tableValue); + } + + return {cellValue}; +}; + +export default LastRowCell; \ No newline at end of file diff --git a/src/components/FormField/components/CriteriaTable/RenderTable/components/NumericFormulaCell/index.js b/src/components/FormField/components/CriteriaTable/RenderTable/components/NumericFormulaCell/index.js new file mode 100644 index 0000000..75f3b3a --- /dev/null +++ b/src/components/FormField/components/CriteriaTable/RenderTable/components/NumericFormulaCell/index.js @@ -0,0 +1,30 @@ +import { InputNumber } from 'primereact/inputnumber'; + +const NumericFormulaCell = ({ getValue, row: { index }, column: { id }, table }) => { + const initialValue = getValue(); + const disabled = table.options.meta?.disabled; + + const onFocus = (e) => { + e.target.select(); + } + + const onChange = (value) => { + table.options.meta?.updateData(index, id, value); + }; + + return ( + onChange(e.value)} + onFocus={onFocus} + minFractionDigits={0} + maxFractionDigits={2} + locale='it-IT' + useGrouping={false} + showButtons + /> + ); +}; + +export default NumericFormulaCell; \ No newline at end of file diff --git a/src/components/FormField/components/CriteriaTable/RenderTable/index.js b/src/components/FormField/components/CriteriaTable/RenderTable/index.js new file mode 100644 index 0000000..822df3a --- /dev/null +++ b/src/components/FormField/components/CriteriaTable/RenderTable/index.js @@ -0,0 +1,101 @@ +import React from 'react'; +import { flexRender, getCoreRowModel, useReactTable } from '@tanstack/react-table'; +import { wrap } from 'object-path-immutable'; +import { head, isEmpty, isNil, pathOr, sum } from 'ramda'; + +// components +import DefaultCell from './components/DefaultCell'; +import LastRowCell from './components/LastRowCell'; +import getNumberFormatted from '../../../../../helpers/getNumberFormatted'; + +const RenderTable = ({ tableValue = {}, columnsCfg, lastRowCfg, setTableValueFn, disabled }) => { + const rows = pathOr([], ['rows'], tableValue) + const table = useReactTable({ + data: rows, + columns: columnsCfg, + defaultColumn: { + cell: DefaultCell + }, + getCoreRowModel: getCoreRowModel(), + meta: { + disabled, + updateData: (rowIndex, columnId, value) => { + const columnCfgData = head(columnsCfg.filter(o => o.accessorKey === columnId)); + const cellData = head(lastRowCfg.filter(o => !isNil(o[columnId]))); + const cellValue = cellData[columnId]; + let newRowsData = wrap(tableValue).set(['rows', rowIndex, columnId], value).value(); + let total = pathOr(0, ['total'], tableValue); + + if (columnCfgData.meta.enableFormula) { + const getAllRowsValues = newRowsData.rows + .map(row => row[columnId]) + .map(v => isEmpty(v) || isNil(v) ? 0 : v); + + if (cellValue === 'sum') { + total = getNumberFormatted(sum(getAllRowsValues)); + } else { + total = 0; + } + } + + newRowsData = wrap(newRowsData).set(['total'], total).value(); + setTableValueFn(newRowsData); + }, + } + }); + + return ( + + + {table.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => { + return ( + + ); + })} + + ))} + + + {table.getRowModel().rows.map((row) => { + return ( + + {row.getVisibleCells().map((cell) => { + return ( + + ); + })} + + ); + })} + + {!isEmpty(lastRowCfg) + ? + {columnsCfg.map((o) => )} + + : null} +
+ {header.isPlaceholder ? null : ( + <> + {flexRender( + header.column.columnDef.header, + header.getContext() + )} + + )} +
+ {flexRender( + cell.column.columnDef.cell, + cell.getContext() + )} +
+ ) +} + +export default RenderTable \ No newline at end of file diff --git a/src/components/FormField/components/CriteriaTable/index.js b/src/components/FormField/components/CriteriaTable/index.js new file mode 100644 index 0000000..f965321 --- /dev/null +++ b/src/components/FormField/components/CriteriaTable/index.js @@ -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 [tableValue, setTableValue] = useState(null); + + const updateValue = useCallback((data) => { + setTableValue(data); + setDataFn(fieldName, data, { shouldValidate: true }); + }, [tableValue, 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.enableFormula || o.fieldtype === 'numeric') { + item.cell = NumericFormulaCell; + } else { + item.cell = (info) => info.getValue(); + } + + return item; + }); + + setColumns(newColumns); + }, [columnsCfg, disabled]); + + useEffect(() => { + setTableValue(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({ rows: rowsData, total: 0 }); + + 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(tableValue, defaultValue)) { + setTableValue(defaultValue); + } + }, [defaultValue]); + + useEffect(() => { + setTableValue(defaultValue); + register(fieldName, properConfig(config)); + }, []); + + return ( + <> + + {tableValue + ? : null} + ) +} + +export default Table; \ No newline at end of file diff --git a/src/components/FormField/components/NumberInput/index.js b/src/components/FormField/components/NumberInput/index.js index 19fc28c..0fec4ce 100644 --- a/src/components/FormField/components/NumberInput/index.js +++ b/src/components/FormField/components/NumberInput/index.js @@ -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 }) => ( 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} diff --git a/src/components/FormField/components/Table/RenderTable/components/DefaultCell/index.js b/src/components/FormField/components/Table/RenderTable/components/DefaultCell/index.js index 6351f92..0df7324 100644 --- a/src/components/FormField/components/Table/RenderTable/components/DefaultCell/index.js +++ b/src/components/FormField/components/Table/RenderTable/components/DefaultCell/index.js @@ -1,24 +1,19 @@ +import { InputText } from 'primereact/inputtext'; + 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 ( - table.options.meta?.updateData(index, id, e.target.value)} - onBlur={onBlur} + disabled={disabled} onFocus={onFocus} - className="w-full px-2 py-1 border rounded" - /> + onChange={(e) => table.options.meta?.updateData(index, id, e.target.value)} /> ); }; diff --git a/src/components/FormField/components/Table/RenderTable/components/LastRowCell/index.js b/src/components/FormField/components/Table/RenderTable/components/LastRowCell/index.js index 3a35e77..4a62335 100644 --- a/src/components/FormField/components/Table/RenderTable/components/LastRowCell/index.js +++ b/src/components/FormField/components/Table/RenderTable/components/LastRowCell/index.js @@ -1,7 +1,8 @@ import { head, isEmpty, isNil, sum } from 'ramda'; +import getNumberFormatted from '../../../../../../../helpers/getNumberFormatted'; -const LastRowCell = ({columnId, lastRows, columnMeta = {}, getColumnDataFn}) => { - const cellData = head(lastRows.filter(o => !isNil(o[columnId]))); +const LastRowCell = ({columnId, lastRowCfg, columnMeta = {}, getColumnDataFn}) => { + const cellData = head(lastRowCfg.filter(o => !isNil(o[columnId]))); let cellValue = cellData[columnId]; if (columnMeta.enableFormula) { @@ -9,7 +10,7 @@ const LastRowCell = ({columnId, lastRows, columnMeta = {}, getColumnDataFn}) => .map(v => isEmpty(v) || isNil(v) ? 0 : v); if (cellValue === 'sum') { - cellValue = sum(getAllRowsValues); + cellValue = getNumberFormatted(sum(getAllRowsValues)); } else { cellValue = 0; } diff --git a/src/components/FormField/components/Table/RenderTable/components/NumericFormulaCell/index.js b/src/components/FormField/components/Table/RenderTable/components/NumericFormulaCell/index.js index 58a6f25..75f3b3a 100644 --- a/src/components/FormField/components/Table/RenderTable/components/NumericFormulaCell/index.js +++ b/src/components/FormField/components/Table/RenderTable/components/NumericFormulaCell/index.js @@ -1,32 +1,28 @@ +import { InputNumber } from 'primereact/inputnumber'; + 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); - } + const onChange = (value) => { + table.options.meta?.updateData(index, id, value); }; return ( - onChange(e.value)} onFocus={onFocus} - onBlur={onBlur} - step="any" - className="w-full px-2 py-1 border rounded" + minFractionDigits={0} + maxFractionDigits={2} + locale='it-IT' + useGrouping={false} + showButtons /> ); }; diff --git a/src/components/FormField/components/Table/RenderTable/index.js b/src/components/FormField/components/Table/RenderTable/index.js index 604a453..c925bcd 100644 --- a/src/components/FormField/components/Table/RenderTable/index.js +++ b/src/components/FormField/components/Table/RenderTable/index.js @@ -7,10 +7,10 @@ import { isEmpty } from 'ramda'; import DefaultCell from './components/DefaultCell'; import LastRowCell from './components/LastRowCell'; -const RenderTable = ({ data, columns, lastRow, setRowsFn, disabled }) => { +const RenderTable = ({ rowsData, columnsCfg, lastRowCfg, setRowsFn, disabled }) => { const table = useReactTable({ - data, - columns, + data: rowsData, + columns: columnsCfg, defaultColumn: { cell: DefaultCell }, @@ -18,7 +18,7 @@ const RenderTable = ({ data, columns, lastRow, setRowsFn, disabled }) => { meta: { disabled, updateData: (rowIndex, columnId, value) => { - const newRowsData = wrap(data).set([rowIndex, columnId], value).value(); + const newRowsData = wrap(rowsData).set([rowIndex, columnId], value).value(); setRowsFn(newRowsData); }, } @@ -68,17 +68,17 @@ const RenderTable = ({ data, columns, lastRow, setRowsFn, disabled }) => { ); })} - {!isEmpty(lastRow) - ? - {columns.map((o) => + {!isEmpty(lastRowCfg) + ? + {columnsCfg.map((o) => )} - + : null} - ) } diff --git a/src/components/FormField/components/Table/index.js b/src/components/FormField/components/Table/index.js index 8ebd19b..02d33be 100644 --- a/src/components/FormField/components/Table/index.js +++ b/src/components/FormField/components/Table/index.js @@ -170,13 +170,13 @@ const Table = ({ ? * : null} {rows ? : null} {!isEmpty(columns) && !shouldDisableNewRows - ?
+ ?
{__('Aggiungi una riga', 'gepafin')}
: null} diff --git a/src/components/FormField/index.js b/src/components/FormField/index.js index c1559a7..6a60658 100644 --- a/src/components/FormField/index.js +++ b/src/components/FormField/index.js @@ -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; diff --git a/src/helpers/getTokens.js b/src/helpers/getTokens.js new file mode 100644 index 0000000..81d40c3 --- /dev/null +++ b/src/helpers/getTokens.js @@ -0,0 +1,11 @@ +import { tokenize } from 'expression-language'; + +const getTokens = expr => tokenize(expr).tokens + .filter((o, i, arr) => o.type === 'name' + && (typeof arr[i+1] === 'undefined' + || (typeof arr[i+1] !== 'undefined' && arr[i+1].value !== '(')) + ) + .map(o => o.value) + .filter((v, i, arr) => arr.indexOf(v) === i); + +export default getTokens; \ No newline at end of file diff --git a/src/helpers/keepKeys.js b/src/helpers/keepKeys.js new file mode 100644 index 0000000..170bbe6 --- /dev/null +++ b/src/helpers/keepKeys.js @@ -0,0 +1,3 @@ +const keepKeys = (arr, keys) => arr.map(obj => Object.fromEntries(keys.map(k => [k, obj[k]]))); + +export default keepKeys; \ No newline at end of file diff --git a/src/helpers/removeKey.js b/src/helpers/removeKey.js new file mode 100644 index 0000000..fc591a9 --- /dev/null +++ b/src/helpers/removeKey.js @@ -0,0 +1,3 @@ +const removeKey = (arr, key) => arr.map(({[key]: _, ...rest}) => rest); + +export default removeKey; \ No newline at end of file diff --git a/src/helpers/renderWithDataVars.js b/src/helpers/renderWithDataVars.js new file mode 100644 index 0000000..ffeb4dc --- /dev/null +++ b/src/helpers/renderWithDataVars.js @@ -0,0 +1,23 @@ +import mustache from 'mustache'; +import { replace } from 'ramda'; + +/** + * Use mustachejs to parse content and replace variables with their values + * Each variable (everything in brackets) is expression for EE + * + * @param {string} content + * @param {object} context + * + * @returns {string} + */ +const renderWithDataVars = (content = '', context = {}) => { + try { + let newContent = replace(/{/g, '{{&', content); + newContent = replace(/}/g, '}}', newContent); + return mustache.render(newContent, context); + } catch { + return content; + } +}; + +export default renderWithDataVars; diff --git a/src/layouts/DefaultLayout/components/AppSidebar/index.js b/src/layouts/DefaultLayout/components/AppSidebar/index.js index 30bc83e..aa25929 100644 --- a/src/layouts/DefaultLayout/components/AppSidebar/index.js +++ b/src/layouts/DefaultLayout/components/AppSidebar/index.js @@ -53,69 +53,97 @@ const AppSidebar = () => { icon: 'pi pi-file', href: '/domande', id: 6, - enable: intersection(permissions, ['VIEW_USERS', 'MANAGE_USERS']).length + enable: intersection(permissions, ['VIEW_USERS', 'MANAGE_USERS', 'ASSIGED_APPLICATION']).length }, { label: __('Domande da valutare', 'gepafin'), icon: 'pi pi-calendar-clock', href: '/domande', id: 7, + enable: intersection(permissions, ['EVALUATE_APPLICATIONS']).length && !intersection(permissions, ['ASSIGED_APPLICATION']).length + }, + { + label: __('Bandi attivi', 'gepafin'), + icon: 'pi pi-file', + href: '/bandi', + id: 8, + enable: intersection(permissions, ['EVALUATE_APPLICATIONS']).length + }, + { + label: __('Soccorso', 'gepafin'), + icon: , + href: '/soccorso-istruttorio', + id: 9, enable: intersection(permissions, ['EVALUATE_APPLICATIONS']).length }, { label: __('Archivio domande', 'gepafin'), icon: 'pi pi-briefcase', href: '/domande', - id: 8, + id: 10, enable: intersection(permissions, ['APPLY_CALLS']).length }, { label: __('Archivio domande', 'gepafin'), icon: 'pi pi-briefcase', href: '/domande-archivio', - id: 9, + id: 11, enable: intersection(permissions, ['VIEW_USERS', 'MANAGE_USERS']).length }, { label: __('Archivio domande', 'gepafin'), icon: 'pi pi-briefcase', href: '/domande-archivio', - id: 10, + id: 12, enable: intersection(permissions, ['EVALUATE_APPLICATIONS']).length }, + { + label: __('Area personale', 'gepafin'), + icon: 'pi pi-calendar-clock', + id: 17, + enable: intersection(permissions, ['ASSIGED_APPLICATION']).length + }, + { + label: __('Domande da valutare', 'gepafin'), + icon: 'pi pi-calendar-clock', + href: '/mie-domande', + id: 18, + enable: intersection(permissions, ['ASSIGED_APPLICATION']).length + }, { label: __('Soccorso istruttorio', 'gepafin'), icon: , - href: '/soccorso-istruttorio', - id: 11, - enable: intersection(permissions, ['EVALUATE_APPLICATIONS']).length + href: '/mio-soccorso-istruttorio', + id: 19, + enable: intersection(permissions, ['ASSIGED_APPLICATION']).length }, { label: __('Gestione utenti', 'gepafin'), icon: 'pi pi-users', href: '/utenti', - id: 12, + id: 13, enable: intersection(permissions, ['VIEW_USERS', 'MANAGE_USERS']).length }, { label: __('Configurazione', 'gepafin'), icon: 'pi pi-cog', //href: '/configurazione', - id: 13, + id: 14, enable: false }, { label: __('Report e Analisi', 'gepafin'), icon: 'pi pi-chart-bar', //href: '/stats', - id: 14, + id: 15, enable: false }, { label: __('Log di Sistema', 'gepafin'), icon: 'pi pi-receipt', - clickFn: () => {}, - id: 15, + clickFn: () => { + }, + id: 16, enable: false } ] @@ -125,20 +153,25 @@ const AppSidebar = () => { {items .filter(o => o.enable) .map(o =>
  • - {o.href - ? - {is(String, o.icon) - ? - : o.icon} - {o.label} - - : } -
  • )} + {o.href + ? + {is(String, o.icon) + ? + : o.icon} + {o.label} + + : (o.clickFn ? + + :
    + {o.label} +
    )} + )} } diff --git a/src/pages/BandiPreInstructor/components/AllBandiTable/index.js b/src/pages/BandiPreInstructor/components/AllBandiTable/index.js new file mode 100644 index 0000000..a5dbe61 --- /dev/null +++ b/src/pages/BandiPreInstructor/components/AllBandiTable/index.js @@ -0,0 +1,149 @@ +import React, { useState, useEffect} from 'react'; +import { __ } from '@wordpress/i18n'; +import { is, uniq } from 'ramda'; + +// tools +import getBandoSeverity from '../../../../helpers/getBandoSeverity'; +import getBandoLabel from '../../../../helpers/getBandoLabel'; +import getDateFromISOstring from '../../../../helpers/getDateFromISOstring'; + +// api +import BandoService from '../../../../service/bando-service'; + +// components +import { FilterMatchMode, FilterOperator } from 'primereact/api'; +import { DataTable } from 'primereact/datatable'; +import { Column } from 'primereact/column'; +import { Dropdown } from 'primereact/dropdown'; +import { Button } from 'primereact/button'; +import { Calendar } from 'primereact/calendar'; +import { Tag } from 'primereact/tag'; +import ProperBandoLabel from '../../../../components/ProperBandoLabel'; +import { Link } from 'react-router-dom'; +import translationStrings from '../../../../translationStringsForComponents'; + + +const AllBandiTable = () => { + const [items, setItems] = useState(null); + const [filters, setFilters] = useState(null); + const [localAsyncRequest, setLocalAsyncRequest] = useState(false); + const [statuses, setStatuses] = useState([]); + + useEffect(() => { + setLocalAsyncRequest(true); + BandoService.getBandi(getCallback, errGetCallbacks); + }, []); + + const getCallback = (data) => { + if (data.status === 'SUCCESS') { + setItems(getFormattedBandiData(data.data)); + setStatuses(uniq(data.data.map(o => o.status))) + initFilters(); + } + setLocalAsyncRequest(false); + } + + const errGetCallbacks = (data) => { + setLocalAsyncRequest(false); + } + + const getFormattedBandiData = (data) => { + return data.map((d) => { + d.dates = d.dates.map(v => is(String, v) ? new Date(v) : (v ? v : '')); + return d; + }); + }; + + const clearFilter = () => { + initFilters(); + }; + + const initFilters = () => { + setFilters({ + global: { value: null, matchMode: FilterMatchMode.CONTAINS }, + name: { operator: FilterOperator.AND, constraints: [{ value: null, matchMode: FilterMatchMode.STARTS_WITH }] }, + start_date: { operator: FilterOperator.AND, constraints: [{ value: null, matchMode: FilterMatchMode.DATE_IS }] }, + end_date: { operator: FilterOperator.AND, constraints: [{ value: null, matchMode: FilterMatchMode.DATE_IS }] }, + status: { operator: FilterOperator.OR, constraints: [{ value: null, matchMode: FilterMatchMode.EQUALS }] }, + }); + }; + + const renderHeader = () => { + return ( +
    +
    + ); + }; + + /*const nameBodyTemplate = (rowData) => { + return {rowData.name} + }*/ + + const dateStartBodyTemplate = (rowData) => { + return getDateFromISOstring(rowData.dates[0]); + }; + + const dateEndBodyTemplate = (rowData) => { + return getDateFromISOstring(rowData.dates[1]); + }; + + const dateFilterTemplate = (options) => { + return options.filterCallback(e.value, options.index)} dateFormat="mm/dd/yy" placeholder="mm/dd/yyyy" mask="99/99/9999" />; + }; + + const statusBodyTemplate = (rowData) => { + return ; + }; + + const statusFilterTemplate = (options) => { + return options.filterCallback(e.value, options.index)} + itemTemplate={statusItemTemplate} + placeholder={translationStrings.selectOneLabel} + className="p-column-filter" + showClear />; + }; + + const statusItemTemplate = (option) => { + return ; + }; + + const actionsBodyTemplate = (rowData) => { + return +
    : { const { id } = useParams(); @@ -55,7 +60,8 @@ const BandoApplicationPreview = () => { trigger, register, getValues, - reset + reset, + watch } = useForm({ defaultValues: useMemo(() => { return formInitialData ? formInitialData : {} @@ -77,6 +83,7 @@ const BandoApplicationPreview = () => { } const activeStepIndex = activeStep - 1; const values = getValues(); + const formValues = watch(); const onValidate = () => { const applId = getApplicationId(); @@ -252,6 +259,43 @@ const BandoApplicationPreview = () => { iconPos="right"/> + useEffect(() => { + let updatedFormValues = klona(formValues); + let context = {}; + + // eslint-disable-next-line array-callback-return + formData.map((o) => { + const variable = head(o.settings.filter(o => o.name === 'variable')); + const formula = head(o.settings.filter(o => o.name === 'formula')); + + if (formula && !isEmpty(formula.value)) { + context = getTokens(formula.value) + .filter(v => !['false', 'null', 'true'].includes(v)) + .reduce((acc, cur) => { + acc[cur] = isNil(context[cur]) ? 0 : context[cur]; + return acc; + }, context); + const mathFormula = renderWithDataVars(formula.value, context); + try { + updatedFormValues[o.id] = evaluate(mathFormula); + } catch (e) { + console.log('Error in math formula: "', mathFormula, '"', e.message); + updatedFormValues[o.id] = 0; + } + } + + 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)) { + reset(updatedFormValues); + } + }, [formValues]); + useEffect(() => { if (formInitialData) { //reset(); @@ -301,9 +345,13 @@ const BandoApplicationPreview = () => { 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')); + let tableColumns = head(o.settings.filter(o => o.name === 'table_columns')); + if (!tableColumns) { + tableColumns = head(o.settings.filter(o => o.name === 'criteria_table_columns')); + } const step = head(o.settings.filter(o => o.name === 'step')); const mime = head(o.settings.filter(o => o.name === 'mime')); + const formula = head(o.settings.filter(o => o.name === 'formula')); let mimeValue = ''; if (mime) { @@ -339,6 +387,7 @@ const BandoApplicationPreview = () => { : { const [forms, setForms] = useState([]); const [formOptions, setFormOptions] = useState([]); const [chosenMainFieldOptions, setChosenMainFieldOptions] = useState([]); - //const [chosenMainField, setChosenMainField] = useState(''); const [mainFieldSuboptions, setMainFieldSubOptions] = useState([]); const [bandoStatus, setBandoStatus] = useState(''); const [isFlowAllowed, setIsFlowAllowed] = useState(true); diff --git a/src/pages/BandoFormsEdit/components/BuilderElement/index.js b/src/pages/BandoFormsEdit/components/BuilderElement/index.js index 8a7b86e..71c0324 100644 --- a/src/pages/BandoFormsEdit/components/BuilderElement/index.js +++ b/src/pages/BandoFormsEdit/components/BuilderElement/index.js @@ -1,7 +1,8 @@ -import React, { useRef } from 'react' +import React, { useEffect, useRef, useState } from 'react' import { useDrag, useDrop } from 'react-dnd' import { ItemTypes } from '../ItemTypes'; import { __ } from '@wordpress/i18n'; +import { head, isEmpty } from 'ramda'; // store import { storeSet, useStore } from '../../../../store'; @@ -14,6 +15,10 @@ import BuilderElementProperLabel from '../BuilderElementProperLabel'; const BuilderElement = ({ id, name, label, index, bandoStatus }) => { const draggingElementId = useStore().main.draggingElementId(); const ref = useRef(null); + const elements = useStore().main.formElements(); + const element = head(elements.filter(o => o.id === id)); + const [isVariable, setIsVariable] = useState('secondary'); + const [isFormula, setIsFormula] = useState('secondary'); const [{ handlerId }, drop] = useDrop({ accept: ItemTypes.FIELD, @@ -97,6 +102,19 @@ const BuilderElement = ({ id, name, label, index, bandoStatus }) => { const opacity = isDragging ? 0 : 1; drag(drop(ref)); + useEffect(() => { + const variable = head(element.settings.filter(o => o.name === 'variable')); + const formula = head(element.settings.filter(o => o.name === 'formula')); + + if (variable && !isEmpty(variable.value)) { + setIsVariable('warning'); + } + + if (formula && !isEmpty(formula.value)) { + setIsFormula('warning'); + } + }, [elements]); + return ( draggingElementId === id ?
    @@ -104,7 +122,13 @@ const BuilderElement = ({ id, name, label, index, bandoStatus }) => {
    :
    - +
    + + {['numberinput', 'criteria_table'].includes(name) + ? : null} + {name === 'numberinput' + ? : null} +
    diff --git a/src/pages/BandoFormsEdit/components/BuilderElementSettings/components/ElementSetting/index.js b/src/pages/BandoFormsEdit/components/BuilderElementSettings/components/ElementSetting/index.js index 67c1bf0..ed7fa42 100644 --- a/src/pages/BandoFormsEdit/components/BuilderElementSettings/components/ElementSetting/index.js +++ b/src/pages/BandoFormsEdit/components/BuilderElementSettings/components/ElementSetting/index.js @@ -1,18 +1,28 @@ -import React from 'react'; +import React, { useEffect, useState } from 'react'; import { __ } from '@wordpress/i18n'; -import { is } from 'ramda'; +import { head, is, isEmpty, isNil, uniq } from 'ramda'; + +// store +import { storeGet } from '../../../../../../store'; + +// tools +import renderHtmlContent from '../../../../../../helpers/renderHtmlContent'; // components import ElementSettingRepeater from '../ElementSettingRepeater'; import { InputText } from 'primereact/inputtext'; import { MultiSelect } from 'primereact/multiselect'; import { Editor } from 'primereact/editor'; - -import { mimeTypes } from '../../../../../../configData'; import ElementSettingTableColumns from '../ElementSettingTableColumns'; import { InputSwitch } from 'primereact/inputswitch'; +import ElementSettingChips from '../ElementSettingChips'; +import ElementSettingCriteriaTableColumns from '../ElementSettingCriteriaTableColumns'; + +import { mimeTypes } from '../../../../../../configData'; + const ElementSetting = ({ setting, changeFn, updateDataFn, bandoStatus }) => { + const [existingVars, setExistingVars] = useState([]); const settingLabels = { label: __('Label', 'gepafin'), @@ -24,6 +34,13 @@ const ElementSetting = ({ setting, changeFn, updateDataFn, bandoStatus }) => { mime: __('Tipo di file', 'gepafin'), text: __('Testo formattato', 'gepafin'), table_columns: '', + criteria_table_columns: '', + variable: __('Variable (only letters and "_")', 'gepafin'), + formula: __('Auto calculation formula', 'gepafin') + } + + const settingDescription = { + formula: __('Create formula using previously declared variables. Use these math operators: +, -, *, /. Example of formula: {entrate}+{assicurazione}.', 'gepafin') } const renderHeader = () => { @@ -60,7 +77,7 @@ const ElementSetting = ({ setting, changeFn, updateDataFn, bandoStatus }) => { options={mimeTypes} optionLabel="name" display="chip" - placeholder={__('Scegli', 'gepafin')} /> + placeholder={__('Scegli', 'gepafin')}/> } else if (setting.name === 'text') { return { name={setting.name} bandoStatus={bandoStatus} setDataFn={updateDataFn}/> + } else if (setting.name === 'criteria_table_columns') { + return } else if (['isRequestedAmount', 'isDelegation'].includes(setting.name)) { return changeFn(e.value, setting.name)}/> + } else if (['variable'].includes(setting.name)) { + return changeFn(value, setting.name)} + value={setting.value}/> } else { return { } } + 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
    {getProperField(setting)} + {setting.name === 'formula' && !isEmpty(existingVars) + ?
    +

    Existing variables: {existingVars.map(v => {`{${v}}`})}

    +
    : null} + {settingDescription[setting.name] + ?
    +

    {renderHtmlContent(settingDescription[setting.name])}

    +
    : null}
    } diff --git a/src/pages/BandoFormsEdit/components/BuilderElementSettings/components/ElementSettingChips/index.js b/src/pages/BandoFormsEdit/components/BuilderElementSettings/components/ElementSettingChips/index.js new file mode 100644 index 0000000..846dcc5 --- /dev/null +++ b/src/pages/BandoFormsEdit/components/BuilderElementSettings/components/ElementSettingChips/index.js @@ -0,0 +1,44 @@ +import React, { useState } from 'react'; + +// components +import { Chips } from 'primereact/chips'; +import { isEmpty } from 'ramda'; + +const ElementSettingChips = ({ restrictedValues = [], changeFn, value = [] }) => { + const [lastTyped, setLastTyped] = useState([]) + + const isValidValue = (newVal) => { + const validationRegex = /^[a-zA-Z_]+$/; + return validationRegex.test(newVal); + }; + + const handleAdd = (e) => { + const newValue = isEmpty(e.value) ? '' : e.value[e.value.length - 1]; + + if (restrictedValues.includes(newValue)) { + changeFn([]); + return; + } + + if (!isValidValue(newValue)) { + changeFn(lastTyped); + return; + } + + setLastTyped(e.value) + changeFn(e.value); + }; + + return ( +
    + +
    + ); +} + +export default ElementSettingChips; \ No newline at end of file diff --git a/src/pages/BandoFormsEdit/components/BuilderElementSettings/components/ElementSettingCriteriaTableColumns/index.js b/src/pages/BandoFormsEdit/components/BuilderElementSettings/components/ElementSettingCriteriaTableColumns/index.js new file mode 100644 index 0000000..cd1a251 --- /dev/null +++ b/src/pages/BandoFormsEdit/components/BuilderElementSettings/components/ElementSettingCriteriaTableColumns/index.js @@ -0,0 +1,329 @@ +import React, { useCallback, useEffect, useState } from 'react'; +import { __, sprintf } from '@wordpress/i18n'; +import { wrap } from 'object-path-immutable'; +import { isEmpty, last, pathOr } from 'ramda'; + +// components +import { InputText } from 'primereact/inputtext'; +import { Button } from 'primereact/button'; +import { InputSwitch } from 'primereact/inputswitch'; +import { Dropdown } from 'primereact/dropdown'; +import { Accordion, AccordionTab } from 'primereact/accordion'; + +// tools +import uniqid from '../../../../../../helpers/uniqid'; +import removeKey from '../../../../../../helpers/removeKey'; + +const ElementSettingCriteriaTableColumns = ({ + value, + name, + setDataFn, + bandoStatus + }) => { + const [stateFieldData, setStateFieldData] = useState([]); + const [rowsData, setRowsData] = useState([]); + + const removeItem = (index) => { + let newData = stateFieldData + .toSpliced(index, 1); + newData = newData.map((o, i) => { + return i === newData.length - 1 + ? {...o, fieldtype: 'numeric', predefined: false, enableFormula: true} + : {...o, fieldtype: 'text', predefined: true, enableFormula: false} + }); + setStateFieldData(newData); + const newRowsData = removeKey(rowsData, last(newData.map(o => o.name))); + setRowsData(newRowsData); + } + + const addNewItem = () => { + setStateFieldData([ + ...stateFieldData.map(o => ({...o, fieldtype: 'text', predefined: true, enableFormula: false})), + { name: uniqid('o'), label: '', fieldtype: 'numeric', predefined: false, enableFormula: true } + ]); + } + + const addNewRow = () => { + const obj = stateFieldData + .filter(o => o.predefined) + .reduce((acc, cur) => { + acc[cur.name] = '' + return acc; + }, {}); + setRowsData([...rowsData, obj]); + } + + const removeRow = (index) => { + const newRowsData = wrap(rowsData).del([index]).value(); + setRowsData(newRowsData); + } + + const onInputChange = (e, index) => { + const { value } = e.target; + const newData = stateFieldData.map((o, i) => { + if (i === index) { + o.label = value; + } + return o; + }) + 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 newData = stateFieldData.map((o, i) => { + if (i === index) { + o.fieldtype = value; + } + return o; + }) + 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 { value } = e.target; + const newRowsData = wrap(rowsData).set([index, name], value).value(); + setRowsData(newRowsData); + } + + const setChecked = (value, index) => { + let name = ''; + const newData = stateFieldData.map((o, i) => { + if (i === index) { + o.predefined = value; + name = o.name; + } + return o; + }); + + let newRowsData = []; + + if (value === false) { + newRowsData = rowsData.map(o => wrap(o).set([name], '').value()); + } else { + newRowsData = rowsData.map(o => wrap(o).set([name], '').value()); + } + + setRowsData(newRowsData); + setStateFieldData(newData); + } + + const setColFormulaChecked = (index) => { + const newData = stateFieldData.map((o, i) => { + if (i === index) { + 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; + }); + setStateFieldData(newData); + } + + const handleClearLastRowData = useCallback(() => { + const newData = stateFieldData.map((o) => { + delete o.lastRowFormula; + o.lastRowText = ''; + delete o.enableFormula; + + return o; + }); + + setStateFieldData(newData); + }, [stateFieldData]); + + const properFields = (item, i) => { + return <> +
    + onInputChange(e, i)}/> +
    +
    + onTypeChange(e.value, i)} + options={[ + { value: 'text', label: __('Testo', 'gepafin') }, + { value: 'numeric', label: __('Numerico', 'gepafin') } + ]}/> +
    +
    + +
    +
    + setChecked(e.value, i)}/> +
    +
    +
    + + } + + const properSubField = (item, i, name) => { + return <> +
    + onSubInputChange(e, name, i)}/> +
    +
    +
    + + } + + const properFieldsLastRow = useCallback((item, i) => { + return <> +
    + {sprintf(__('Colonna %d'), i + 1)} +
    + {item.enableFormula + ?
    + onLastRowFormulaChange(e.value, i)} + options={[ + { value: 'sum', label: __('Somma automatica', 'gepafin') } + ]}/> +
    + :
    + onLastRowInputChange(e, i)}/> +
    } + + }, [stateFieldData]); + + const lastRow =
    +
    +
    + {__('Definisci ultima righa', 'gepafin')} +
    +
    + {stateFieldData.map((o, i) =>
    + {properFieldsLastRow(o, i)} +
    )} +
    ; + + useEffect(() => { + const stateFieldData = pathOr([], ['stateFieldData'], value); + setStateFieldData(stateFieldData); + const rowsData = pathOr([], ['rowsData'], value); + setRowsData(rowsData); + }, []); + + useEffect(() => { + setDataFn(name, { + stateFieldData, + rowsData + }); + }, [stateFieldData, rowsData]); + + return ( + <> +
    + {stateFieldData.length > 0 + ?
    +
    {__('Colonne', 'gepafin')}
    +
    {__('Tipo', 'gepafin')}
    +
    {__('Calcola', 'gepafin')}
    +
    {__('Predefinito?', 'gepafin')}
    +
    +
    : null} + {stateFieldData.map((o, i) =>
    + {properFields(o, i)} +
    )} +
    + {stateFieldData + .filter(o => o.predefined).length > 0 + ?
    + + {stateFieldData + //.filter(o => o.predefined) + .map((o, i) => + o.predefined + ? +
    + {rowsData.map((c, k) => { + return
    + {properSubField(c, k, o.name)} +
    + })} +
    +
    : null)} +
    +
    + : null} + {lastRow} + + ) +} + +export default ElementSettingCriteriaTableColumns; \ No newline at end of file diff --git a/src/pages/BandoFormsEdit/components/BuilderElementSettings/components/ElementSettingTableColumns-old/index.js b/src/pages/BandoFormsEdit/components/BuilderElementSettings/components/ElementSettingTableColumns-old/index.js deleted file mode 100644 index ee5d9aa..0000000 --- a/src/pages/BandoFormsEdit/components/BuilderElementSettings/components/ElementSettingTableColumns-old/index.js +++ /dev/null @@ -1,159 +0,0 @@ -import React, { useEffect, useState } from 'react'; -import { __ } from '@wordpress/i18n'; -import { wrap } from 'object-path-immutable'; -import { pathOr } from 'ramda'; - -// components -import { InputText } from 'primereact/inputtext'; -import { Button } from 'primereact/button'; -import { InputSwitch } from 'primereact/inputswitch'; - -// tools -import uniqid from '../../../../../../helpers/uniqid'; - -const ElementSettingTableColumns = ({ - value, - name, - setDataFn, - bandoStatus - }) => { - const [stateFieldData, setStateFieldData] = useState([]); - const [rowsData, setRowsData] = useState([]); - - const removeItem = (index) => { - const newData = stateFieldData.toSpliced(index, 1); - setStateFieldData(newData); - } - - const addNewItem = () => { - setStateFieldData([...stateFieldData, { name: uniqid('o'), label: '', predefined: false }]); - } - - const addNewRow = () => { - const obj = stateFieldData - .filter(o => o.predefined) - .reduce((acc, cur) => { - acc[cur.name] = '' - return acc; - }, {}); - setRowsData([...rowsData, obj]); - } - - const removeRow = (index) => { - const newRowsData = wrap(rowsData).del([index]).value(); - setRowsData(newRowsData); - } - - const onInputChange = (e, index) => { - const { value } = e.target; - const newData = stateFieldData.map((o, i) => { - if (i === index) { - o.label = value; - } - return o; - }) - setStateFieldData(newData); - } - - const onSubInputChange = (e, name, index) => { - const { value } = e.target; - const newRowsData = wrap(rowsData).set([index, name], value).value(); - setRowsData(newRowsData); - } - - const setChecked = (value, index) => { - let name = ''; - const newData = stateFieldData.map((o, i) => { - if (i === index) { - o.predefined = value; - name = o.name; - } - return o; - }); - - let newRowsData = []; - - if (value === false) { - newRowsData = rowsData.map(o => wrap(o).set([name], '').value()); - } else { - newRowsData = rowsData.map(o => wrap(o).set([name], '').value()); - } - - setRowsData(newRowsData); - setStateFieldData(newData); - } - - const properField = (item, i) => { - return <> - onInputChange(e, i)}/> -
    - {__('Predefinito?', 'gepafin')} - setChecked(e.value, i)}/> -
    - - } - - const properSubField = (item, i, name) => { - return onSubInputChange(e, name, i)}/> - } - - useEffect(() => { - const stateFieldData = pathOr([], ['stateFieldData'], value); - setStateFieldData(stateFieldData); - const rowsData = pathOr([], ['rowsData'], value); - setRowsData(rowsData); - }, []); - - useEffect(() => { - setDataFn(name, { - stateFieldData, - rowsData - }); - }, [stateFieldData, rowsData]); - - stateFieldData.filter(o => o.predefined) - - return ( - <> -
    - {stateFieldData.map((o, i) =>
    -
    - {properField(o, i)} -
    -
    )} -
    - {stateFieldData - .filter(o => o.predefined) - .map((o, i) =>
    -
    - -
    - {rowsData.map((c, k) => { - return
    -
    - {properSubField(c, k, o.name)} -
    -
    - })} -
    -
    -
    )} - - ) -} - -export default ElementSettingTableColumns; \ No newline at end of file diff --git a/src/pages/BandoFormsEdit/components/BuilderElementSettings/components/ElementSettingTableColumns/index.js b/src/pages/BandoFormsEdit/components/BuilderElementSettings/components/ElementSettingTableColumns/index.js index 1806e6d..cb2337a 100644 --- a/src/pages/BandoFormsEdit/components/BuilderElementSettings/components/ElementSettingTableColumns/index.js +++ b/src/pages/BandoFormsEdit/components/BuilderElementSettings/components/ElementSettingTableColumns/index.js @@ -1,17 +1,18 @@ import React, { useCallback, useEffect, useState } from 'react'; import { __, sprintf } from '@wordpress/i18n'; import { wrap } from 'object-path-immutable'; -import { isEmpty, pathOr } from 'ramda'; +import { isEmpty, last, pathOr } from 'ramda'; // components import { InputText } from 'primereact/inputtext'; import { Button } from 'primereact/button'; import { InputSwitch } from 'primereact/inputswitch'; +import { Dropdown } from 'primereact/dropdown'; +import { Accordion, AccordionTab } from 'primereact/accordion'; // tools import uniqid from '../../../../../../helpers/uniqid'; -import { Dropdown } from 'primereact/dropdown'; -import { Accordion, AccordionTab } from 'primereact/accordion'; +import removeKey from '../../../../../../helpers/removeKey'; const ElementSettingTableColumns = ({ value, @@ -25,6 +26,8 @@ const ElementSettingTableColumns = ({ const removeItem = (index) => { const newData = stateFieldData.toSpliced(index, 1); setStateFieldData(newData); + const newRowsData = removeKey(rowsData, last(newData.map(o => o.name))); + setRowsData(newRowsData); } const addNewItem = () => { @@ -158,7 +161,7 @@ const ElementSettingTableColumns = ({
    onTypeChange(e.value, i)} options={[ @@ -168,6 +171,7 @@ const ElementSettingTableColumns = ({
    @@ -212,6 +217,7 @@ const ElementSettingTableColumns = ({ {item.enableFormula ?
    onLastRowFormulaChange(e.value, i)} options={[ @@ -232,6 +238,7 @@ const ElementSettingTableColumns = ({ {__('Definisci ultima righa', 'gepafin')}
    {stateFieldData .filter(o => o.predefined).length > 0 diff --git a/src/pages/BandoFormsEdit/components/BuilderElementSettings/index.js b/src/pages/BandoFormsEdit/components/BuilderElementSettings/index.js index 3e4f66a..e95efd6 100644 --- a/src/pages/BandoFormsEdit/components/BuilderElementSettings/index.js +++ b/src/pages/BandoFormsEdit/components/BuilderElementSettings/index.js @@ -110,22 +110,6 @@ const BuilderElementSettings = ({ closeSettingsFn, bandoStatus }) => { return dynamicDataOptions[type] ?? []; } - /*const searchDynamicTags = (event) => { - const type = activeElementData.name; - const available = dynamicDataOptions[type]; - let filtered; - - if (!event.query.trim().length) { - filtered = [...available]; - } else { - filtered = available.filter((tag) => { - return tag.label.toLowerCase().startsWith(event.query.toLowerCase()); - }); - } - - setFilteredDynamicDataOptions(filtered); - }*/ - useEffect(() => { const chosen = head(elements.filter(o => o.id === activeElement)); const elementItems = storeGet.main.elementItems(); @@ -160,12 +144,14 @@ const BuilderElementSettings = ({ closeSettingsFn, bandoStatus }) => { {settings - ? settings.map((o) => ) + ? settings + .filter(o => !['variable', 'formula'].includes(o.name)) + .map((o) => ) : null} {!isNil(dynamicDataOptions[activeElementData.name]) ?
    @@ -248,6 +234,21 @@ const BuilderElementSettings = ({ closeSettingsFn, bandoStatus }) => { placeholder={__('Scegli', 'gepafin')}/>
    + {settings + && settings + .filter(o => ['variable', 'formula'].includes(o.name)).length > 0 + ? + {settings + ? settings + .filter(o => ['variable', 'formula'].includes(o.name)) + .map((o) => ) + : null} + : null}
    + + + + + + + + + + + + ); + }; + + const header = renderHeader(); + + const updateEvaluationValue = (value, path, maxValue) => { + let finalValue = value; + + if (maxValue) { + finalValue = value > maxValue ? maxValue : value; + } + + const newData = wrap(formData).set(path.split('.'), finalValue).value(); + + setFormData(newData); + } + + const doCreate = () => { + storeSet.main.setAsyncRequest(); + + AmendmentsService.createSoccorso(formData, createCallback, errCreateCallback, [ + ['applicationEvaluationId', evaluationId] + ]); + } + + const createCallback = (data) => { + if (data.status === 'SUCCESS') { + if (toast.current) { + toast.current.show({ + severity: 'success', + summary: '', + detail: data.message + }); + } + setTimeout(() => { + navigate(`/mie-domande/${id}/soccorso/${data.data.id}`); + }, 1000) + } + storeSet.main.unsetAsyncRequest(); + } + + const errCreateCallback = (data) => { + if (toast.current && data.message) { + toast.current.show({ + severity: 'error', + summary: '', + detail: data.message + }); + } + set404FromErrorResponse(data); + storeSet.main.unsetAsyncRequest(); + } + + const initCreationProcess = () => { + setIsVisibleConfirmDialog(true); + } + + const headerConfirmDialog = () => { + return {__('Richiesta di conferma', 'gepafin')}; + } + + const hideConfirmDialog = () => { + setIsVisibleConfirmDialog(false); + } + + const footerConfirmDialog = () => { + return
    +
    + } + + const doConfirm = () => { + setIsVisibleConfirmDialog(false); + doCreate(); + } + + return ( +
    +
    +

    {__('Richiesta Integrazione Documentale', 'gepafin')}

    +
    + +
    + + + +
    +
    + +
    + + {!isAsyncRequest && !isEmpty(data) + ?
    +
    +

    + {__('ID domanda', 'gepafin')} + {data.applicationId} +

    +

    + {__('Bando', 'gepafin')} + {data.callName} +

    +

    + {__('Referente Aziendale', 'gepafin')} + {data.beneficiaryName} +

    +
    + +
    +
    +
    +

    {__('Pec/Email', 'gepafin')}

    +
    + updateEvaluationValue( + e.htmlValue, + 'note' + )} + style={{ height: 80 * 3, width: '100%' }} + /> +
    + +

    {__('Tempo per la Risposta (giorni)', 'gepafin')}

    +
    + updateEvaluationValue( + e.value, + 'responseDays', + 9999 + )}/> +
    + +

    {__('Notifica', 'gepafin')}

    +
    +
    + updateEvaluationValue( + e.value, + 'isSendEmail' + )}/> + +
    +
    + updateEvaluationValue( + e.value, + 'isSendNotification' + )}/> + +
    +
    +
    + {formData.formFields + ?
    +

    {__('Documenti da Integrare', 'gepafin')}

    +
    +
    + {formData.formFields.map((o, i) =>
    + updateEvaluationValue( + e.checked, + `formFields.${i}.selected` + )} + checked={o.selected}> + +
    )} +
    +
    +
    + : null} +
    +
    + +
    + + {__('Attenzione', 'gepafin')} + {__("L'invio della richiesta di integrazione sospenderà il termine di valutazione della domanda.", 'gepafin')} +
    + +
    + {__('Azioni', 'gepafin')} +
    + +
    +
    +
    +
    + + +
    +

    {__('Soccorso istruttorio autorizzato dal direttore e autorizzazione caricata su portale a seguito del quale parte l\'email?', 'gepafin')}

    +
    +
    + +
    + : <> + + + + + + + + + } +
    + ) + +} + +export default SoccorsoAddInstructorManager; diff --git a/src/pages/SoccorsoEditInstructorManager/index.js b/src/pages/SoccorsoEditInstructorManager/index.js new file mode 100644 index 0000000..6726001 --- /dev/null +++ b/src/pages/SoccorsoEditInstructorManager/index.js @@ -0,0 +1,587 @@ +import React, { useState, useEffect, useRef, useMemo } from 'react'; +import { __ } from '@wordpress/i18n'; +import { useNavigate, useParams } from 'react-router-dom'; +import { is, isEmpty } from 'ramda'; +import { wrap } from 'object-path-immutable'; +import { klona } from 'klona'; +import { useForm } from 'react-hook-form'; + +// store +import { storeSet, useStore } from '../../store'; + +// api +import AmendmentsService from '../../service/amendments-service'; + +// tools +import set404FromErrorResponse from '../../helpers/set404FromErrorResponse'; +import getBandoLabel from '../../helpers/getBandoLabel'; +import getDateFromISOstring from '../../helpers/getDateFromISOstring'; +import getEmailTemplateForSoccorso from '../../helpers/getStrippedHtmlBodyTags'; + +// components +import { Button } from 'primereact/button'; +import BlockingOverlay from '../../components/BlockingOverlay'; +import { Toast } from 'primereact/toast'; +import { classNames } from 'primereact/utils'; +import { Dialog } from 'primereact/dialog'; +import FormField from '../../components/FormField'; +import { Editor } from 'primereact/editor'; +import { InputNumber } from 'primereact/inputnumber'; +import SoccorsoComunications from '../SoccorsoEditPreInstructor/components/SoccorsoComunications'; + + +const SoccorsoEditInstructorManager = () => { + const isAsyncRequest = useStore().main.isAsyncRequest(); + const { id, amendmentId } = useParams(); + const navigate = useNavigate(); + const [data, setData] = useState({}); + const [isVisibleCloseAmendDialog, setIsVisibleCloseAmendDialog] = useState(false); + const [isVisibleExtendTimeDialog, setIsVisibleExtendTimeDialog] = useState(false); + const [extendedTime, setExtendedTime] = useState(3); + const [isLoadingExtendingTime, setIsLoadingExtendingTime] = useState(false); + const [isLoadingReminding, setIsLoadingReminding] = useState(false); + const [internalNote, setInternalNote] = useState(''); + const toast = useRef(null); + const [formInitialData, setFormInitialData] = useState({}); + const { + control, + handleSubmit, + formState: { errors }, + setValue, + register, + trigger, + getValues + } = useForm({ + defaultValues: useMemo(() => { + return formInitialData; + }, [formInitialData]), mode: 'onChange' + }); + + const goToEvaluationPage = () => { + navigate(`/mie-domande/${id}`); + } + + const getCallback = (data) => { + if (data.status === 'SUCCESS') { + setData(getFormattedData(data.data)); + let formDataInitial = data.data.applicationFormFields.reduce((acc, cur) => { + if (cur.fieldValue) { + acc[cur.fieldId] = cur.fieldValue; + } + return acc; + }, {}); + formDataInitial = { + ...formDataInitial, + amendmentDocuments: data.data.amendmentDocuments + } + setFormInitialData(formDataInitial); + } + storeSet.main.unsetAsyncRequest(); + } + + const errGetCallback = (data) => { + if (toast.current && data.message) { + toast.current.show({ + severity: 'error', + summary: '', + detail: data.message + }); + } + set404FromErrorResponse(data); + storeSet.main.unsetAsyncRequest(); + } + + const getFormattedData = (data) => { + data.startDate = is(String, data.startDate) ? new Date(data.startDate) : (data.startDate ? data.startDate : ''); + data.expirationDate = is(String, data.expirationDate) ? new Date(data.expirationDate) : (data.expirationDate ? data.expirationDate : ''); + return data; + }; + + const renderHeader = () => { + return ( + + + + + + + + + + + + + + ); + }; + + const header = renderHeader(); + + const updateNewAmendmentData = (value, path) => { + const newData = wrap(data).set(path, value).value(); + setData(newData); + } + + const onSubmit = () => { + }; + + const doUpdateAmendment = (doClose = false) => { + trigger(); + let formValues = klona(getValues()); + const newFormValues = Object.keys(formValues) + .filter(v => v !== 'amendmentDocuments') + .reduce((acc, cur) => { + let fieldVal = formValues[cur]; + + fieldVal = isEmpty(fieldVal) ? null : fieldVal; + fieldVal = is(Array, fieldVal) ? fieldVal.map(o => o.id).join(',') : null; + + acc.push({ + 'fieldId': cur, + 'fieldValue': fieldVal + }); + return acc; + }, []); + const newAmendDocs = formValues.amendmentDocuments + ? formValues.amendmentDocuments.map(o => o.id).join(',') + : ''; + + const submitData = { + applicationFormFields: newFormValues, + amendmentDocuments: newAmendDocs, + amendmentNotes: data.amendmentNotes + } + + storeSet.main.setAsyncRequest(); + AmendmentsService.updateSoccorso( + amendmentId, + submitData, + (resp) => updateAmendmentCallback(resp, doClose), + errUpdateAmendmentCallback + ); + } + + const updateAmendmentCallback = (data, doClose = false) => { + if (data.status === 'SUCCESS') { + setData(getFormattedData(data.data)); + + if (doClose) { + const submitData = { + internalNote + } + storeSet.main.setAsyncRequest(); + AmendmentsService.closeSoccorso(amendmentId, submitData, closeAmendmentCallback, errCloseAmendmentCallback); + } else { + if (toast.current) { + toast.current.show({ + severity: 'success', + summary: '', + detail: data.message + }); + } + let formDataInitial = data.data.applicationFormFields.reduce((acc, cur) => { + if (cur.fieldValue) { + acc[cur.fieldId] = cur.fieldValue; + } + return acc; + }, formInitialData); + formDataInitial = { + ...formDataInitial, + amendmentDocuments: data.data.amendmentDocuments + } + setFormInitialData(formDataInitial); + } + } + storeSet.main.unsetAsyncRequest(); + } + + const errUpdateAmendmentCallback = (data) => { + if (toast.current && data.message) { + toast.current.show({ + severity: 'error', + summary: '', + detail: data.message + }); + } + set404FromErrorResponse(data); + storeSet.main.unsetAsyncRequest(); + } + + const openCloseAmendmentDialog = () => { + setIsVisibleCloseAmendDialog(true); + } + + const headerCloseAmendDialog = () => { + return {__('Chiudi Soccorso Istruttorio', 'gepafin')} + } + + const hideCloseAmendDialog = () => { + setIsVisibleCloseAmendDialog(false); + } + + const footerCloseAmendDialog = () => { + return
    +
    + } + + const doCloseAmendment = () => { + doUpdateAmendment(true); + } + + const closeAmendmentCallback = (data) => { + if (data.status === 'SUCCESS') { + if (toast.current) { + toast.current.show({ + severity: 'success', + summary: '', + detail: data.message + }); + } + if (data.data.status) { + updateNewAmendmentData(data.data.status, ['status']); + setIsVisibleCloseAmendDialog(false); + } + } + storeSet.main.unsetAsyncRequest(); + } + + const errCloseAmendmentCallback = (data) => { + if (toast.current && data.message) { + toast.current.show({ + severity: 'error', + summary: '', + detail: data.message + }); + } + set404FromErrorResponse(data); + storeSet.main.unsetAsyncRequest(); + } + + const headerExtendRespDialog = () => { + return {__('Estendi scadenza', 'gepafin')} + } + + const hideExtendRespDialog = () => { + setIsVisibleExtendTimeDialog(false); + } + + const footerExtendRespDialog = () => { + return
    +
    + } + + const openExtendResponseTimeDialog = () => { + setIsVisibleExtendTimeDialog(true); + setExtendedTime(3); + } + + const doExtendTimeResponse = () => { + setIsLoadingExtendingTime(true); + AmendmentsService.extendSoccorso(amendmentId, extendedTime, extendCallback, errExtendCallback); + } + + const extendCallback = (data) => { + if (data.status === 'SUCCESS') { + if (toast.current) { + toast.current.show({ + severity: 'success', + summary: '', + detail: data.message + }); + } + setIsVisibleExtendTimeDialog(false); + } + setIsLoadingExtendingTime(false); + } + + const errExtendCallback = (data) => { + if (toast.current && data.message) { + toast.current.show({ + severity: 'error', + summary: '', + detail: data.message + }); + } + set404FromErrorResponse(data); + setIsLoadingExtendingTime(false); + } + + const sendReminder = () => { + setIsLoadingReminding(true); + AmendmentsService.sendReminderForSoccorso(amendmentId, reminderCallback, errReminderCallback) + } + + const reminderCallback = (data) => { + if (data.status === 'SUCCESS') { + if (toast.current) { + toast.current.show({ + severity: 'success', + summary: '', + detail: data.message + }); + } + } + setIsLoadingReminding(false); + } + + const errReminderCallback = (data) => { + if (toast.current && data.message) { + toast.current.show({ + severity: 'error', + summary: '', + detail: data.message + }); + } + set404FromErrorResponse(data); + setIsLoadingReminding(false); + } + + useEffect(() => { + if (formInitialData) { + Object.keys(formInitialData).map(k => setValue(k, formInitialData[k])); + trigger(); + } + }, [formInitialData]); + + useEffect(() => { + const parsedSoccorsoId = parseInt(amendmentId); + const soccorsoEntityId = !isNaN(parsedSoccorsoId) ? parsedSoccorsoId : 0; + + AmendmentsService.getSoccorsoById(getCallback, errGetCallback, [['id', soccorsoEntityId]]); + }, [amendmentId]); + + return ( +
    +
    +

    {__('Soccorso Istruttorio - Dettagli', 'gepafin')}

    +
    + +
    + + + +
    +
    + +
    + +
    +
    +

    + {__('ID domanda', 'gepafin')} + {data.applicationId} +

    +

    + {__('Bando', 'gepafin')} + {data.callName} +

    +

    + {__('Referente Aziendale', 'gepafin')} + {data.beneficiaryName} +

    +

    + {__('Inizio', 'gepafin')} + {getDateFromISOstring(data.startDate)} +

    +

    + {__('Scadenza', 'gepafin')} + {getDateFromISOstring(data.expirationDate)} +

    +

    + {__('Stato', 'gepafin')} + {getBandoLabel(data.status)} +

    +
    + +
    +

    {__('Dettagli richiesta', 'gepafin')}

    +

    {__('Note e spiegazioni', 'gepafin')}

    +
    {getEmailTemplateForSoccorso(data.emailTemplate, data.note)}
    +
    +
    +

    {__('Documenti richiesti', 'gepafin')}

    +
      + {data.formFields + ? data.formFields.map((o, i) =>
    1. + {o.label} +
    2. ) : null} +
    +
    + +
    +

    {__('Comunicazioni', 'gepafin')}

    + +
    + + {data.formFields && !isEmpty(data.formFields) + ?
    +

    {__('Documenti Ricevuti', 'gepafin')}

    + +
    + {data.formFields.map((o, i) => { + return + })} + +
    : null} + +
    +

    {__('Documenti aggiuntivi', 'gepafin')}

    +
    +

    {__('Notes', 'gepafin')}

    +
    + + updateNewAmendmentData( + e.htmlValue, + 'amendmentNotes' + )} + style={{ height: 80 * 3, width: '100%' }} + /> +
    + +
    +
    + +
    + +
    + {__('Azioni', 'gepafin')} +
    + +
    +
    +
    +
    + +
    + + +
    + + setExtendedTime(e.value)}/> +
    +
    + + +
    + +
    + + setInternalNote(e.htmlValue)} + style={{ height: 80 * 3, width: '100%' }} + /> +
    +
    +
    +
    + ) + +} + +export default SoccorsoEditInstructorManager; diff --git a/src/pages/SoccorsoIstruttorioPreInstructor/index.js b/src/pages/SoccorsoIstruttorioPreInstructor/index.js index 6a92b97..4adb445 100644 --- a/src/pages/SoccorsoIstruttorioPreInstructor/index.js +++ b/src/pages/SoccorsoIstruttorioPreInstructor/index.js @@ -34,12 +34,6 @@ const SoccorsoIstruttorioPreInstructor = () => {
    -
    - -
    - -
    -

    {__('Riepilogo', 'gepafin')}

    @@ -88,6 +82,12 @@ const SoccorsoIstruttorioPreInstructor = () => {
    + +
    + +
    + +
    ) } diff --git a/src/routes.js b/src/routes.js index c8c0302..e72c0c2 100644 --- a/src/routes.js +++ b/src/routes.js @@ -41,6 +41,13 @@ import DomandeInstructorManager from './pages/DomandeInstructorManager'; import DomandaEditInstructorManager from './pages/DomandaEditInstructorManager'; import UserActivity from './pages/UserActivity'; import DomandeArchive from './pages/DomandeArchive'; +import BandiPreInstructor from './pages/BandiPreInstructor'; +import BandoViewPreInstructor from './pages/BandoViewPreInstructor'; +import DomandeArchivePreInstructor from './pages/DomandeArchivePreInstructor'; +import DashboardInstructorManager from './pages/DashboardInstructorManager'; +import DomandeMieInstructorManager from './pages/DomandeMieInstructorManager'; +import SoccorsoAddInstructorManager from './pages/SoccorsoAddInstructorManager'; +import SoccorsoEditInstructorManager from './pages/SoccorsoEditInstructorManager'; const routes = ({ role, chosenCompanyId }) => { @@ -51,19 +58,19 @@ const routes = ({ role, chosenCompanyId }) => { {'ROLE_SUPER_ADMIN' === role ? : null} {'ROLE_BENEFICIARY' === role ? : null} {'ROLE_PRE_INSTRUCTOR' === role ? : null} - {'ROLE_INSTRUCTOR_MANAGER' === role ? : null} + {'ROLE_INSTRUCTOR_MANAGER' === role ? : null} }/> {'ROLE_SUPER_ADMIN' === role ? : null} {'ROLE_BENEFICIARY' === role ? : null} - {'ROLE_PRE_INSTRUCTOR' === role ? : null} - {'ROLE_INSTRUCTOR_MANAGER' === role ? : null} + {'ROLE_PRE_INSTRUCTOR' === role ? : null} + {'ROLE_INSTRUCTOR_MANAGER' === role ? : null} }/> {'ROLE_SUPER_ADMIN' === role ? : null} {'ROLE_BENEFICIARY' === role ? : null} - {'ROLE_PRE_INSTRUCTOR' === role ? : null} - {'ROLE_INSTRUCTOR_MANAGER' === role ? : null} + {'ROLE_PRE_INSTRUCTOR' === role ? : null} + {'ROLE_INSTRUCTOR_MANAGER' === role ? : null} }/> {'ROLE_SUPER_ADMIN' === role ? : null} @@ -117,7 +124,7 @@ const routes = ({ role, chosenCompanyId }) => { {'ROLE_SUPER_ADMIN' === role ? : null} {'ROLE_BENEFICIARY' === role ? : null} {'ROLE_PRE_INSTRUCTOR' === role ? : null} - {'ROLE_INSTRUCTOR_MANAGER' === role ? : null} + {'ROLE_INSTRUCTOR_MANAGER' === role ? : null} }/> {'ROLE_SUPER_ADMIN' === role ? : null} @@ -128,7 +135,7 @@ const routes = ({ role, chosenCompanyId }) => { {'ROLE_SUPER_ADMIN' === role ? : null} {'ROLE_BENEFICIARY' === role ? : null} - {'ROLE_PRE_INSTRUCTOR' === role ? : null} + {'ROLE_PRE_INSTRUCTOR' === role ? : null} {'ROLE_INSTRUCTOR_MANAGER' === role ? : null} }/> @@ -141,7 +148,7 @@ const routes = ({ role, chosenCompanyId }) => { {'ROLE_SUPER_ADMIN' === role ? : null} {'ROLE_BENEFICIARY' === role ? : null} {'ROLE_PRE_INSTRUCTOR' === role ? : null} - {'ROLE_INSTRUCTOR_MANAGER' === role ? : null} + {'ROLE_INSTRUCTOR_MANAGER' === role ? : null} }/> {'ROLE_SUPER_ADMIN' === role ? : null} @@ -155,6 +162,36 @@ const routes = ({ role, chosenCompanyId }) => { {'ROLE_PRE_INSTRUCTOR' === role ? : null} {'ROLE_INSTRUCTOR_MANAGER' === role ? : null} }/> + + {'ROLE_SUPER_ADMIN' === role ? : null} + {'ROLE_BENEFICIARY' === role ? : null} + {'ROLE_PRE_INSTRUCTOR' === role ? : null} + {'ROLE_INSTRUCTOR_MANAGER' === role ? : null} + }/> + + {'ROLE_SUPER_ADMIN' === role ? : null} + {'ROLE_BENEFICIARY' === role ? : null} + {'ROLE_PRE_INSTRUCTOR' === role ? : null} + {'ROLE_INSTRUCTOR_MANAGER' === role ? : null} + }/> + + {'ROLE_SUPER_ADMIN' === role ? : null} + {'ROLE_BENEFICIARY' === role ? : null} + {'ROLE_PRE_INSTRUCTOR' === role ? : null} + {'ROLE_INSTRUCTOR_MANAGER' === role ? : null} + }/> + + {'ROLE_SUPER_ADMIN' === role ? : null} + {'ROLE_BENEFICIARY' === role ? : null} + {'ROLE_PRE_INSTRUCTOR' === role ? : null} + {'ROLE_INSTRUCTOR_MANAGER' === role ? : null} + }/> + + {'ROLE_SUPER_ADMIN' === role ? : null} + {'ROLE_BENEFICIARY' === role ? : null} + {'ROLE_PRE_INSTRUCTOR' === role ? : null} + {'ROLE_INSTRUCTOR_MANAGER' === role ? : null} + }/> {'ROLE_SUPER_ADMIN' === role ? : null} {'ROLE_BENEFICIARY' === role ? : null} diff --git a/src/tempData.js b/src/tempData.js index 372def6..bc8df5f 100644 --- a/src/tempData.js +++ b/src/tempData.js @@ -92,7 +92,15 @@ export const elementItems = [ { name: "isRequestedAmount", value: false - } + }, + { + name: "variable", + value: [] + }, + { + name: "formula", + value: "" + }, ], validators: { isRequired: false, @@ -427,5 +435,29 @@ export const elementItems = [ validators: { custom: 'nonEmptyTables' } + }, + { + id: 21, + sortOrder: 21, + name: 'criteria_table', + label: 'Tabella di criteri', + description: 'Tabella di criteri', + settings: [ + { + name: "label", + value: "Tabella" + }, + { + name: "criteria_table_columns", + value: {} + }, + { + name: "variable", + value: [] + } + ], + validators: { + custom: 'nonEmptyTables' + } } ]