From 5d740c1069f15b735fe461f39835fbb8c291439b Mon Sep 17 00:00:00 2001 From: Vitalii Kiiko Date: Wed, 15 Jan 2025 11:50:35 +0100 Subject: [PATCH 01/27] - added pagination for notifications (non beneficiary); --- .../NotificationItemChosen/index.js | 5 +- src/components/NotificationsSidebar/index.js | 177 +++++++++++++----- src/components/PaginatorBasic/index.js | 59 ++++++ .../components/AppSidebar/index.js | 22 +-- .../Bandi/components/AllBandiTable/index.js | 2 +- .../components/AllBandiAccordion/index.js | 2 +- .../AllBandiPreferredAccordion/index.js | 2 +- .../DraftApplicationsTable/index.js | 2 +- .../components/LatestBandiTable/index.js | 33 +++- .../LatestUsersActivityTable/index.js | 2 +- .../components/LatestBandiTable/index.js | 2 +- .../MyLatestSubmissionsTable/index.js | 2 +- .../PreInstructorDomandeTable/index.js | 2 +- .../components/AllDomandeTable/index.js | 2 +- .../AllDomandeArchiveTable/index.js | 5 +- .../BeneficiarioDomandeTable/index.js | 2 +- .../PreInstructorSoccorsiTable/index.js | 2 +- .../Users/components/AllUsersTable/index.js | 2 +- src/service/bando-service.js | 4 + src/service/notification-service.js | 8 + 20 files changed, 259 insertions(+), 78 deletions(-) create mode 100644 src/components/PaginatorBasic/index.js diff --git a/src/components/NotificationsSidebar/components/NotificationItemChosen/index.js b/src/components/NotificationsSidebar/components/NotificationItemChosen/index.js index 4588de6..24ecc1e 100644 --- a/src/components/NotificationsSidebar/components/NotificationItemChosen/index.js +++ b/src/components/NotificationsSidebar/components/NotificationItemChosen/index.js @@ -17,12 +17,13 @@ const NotificationItemChosen = ({ item, closeFn, markReadFn }) => { {getDateFromISOstring(item.createdDate)} {item.message} - + + ({currentPage} {__('di', 'gepafin')} {totalPages}) + + + : null + ) +} + +export default PaginatorBasic; \ No newline at end of file diff --git a/src/layouts/DefaultLayout/components/AppSidebar/index.js b/src/layouts/DefaultLayout/components/AppSidebar/index.js index 5c81b8f..30bc83e 100644 --- a/src/layouts/DefaultLayout/components/AppSidebar/index.js +++ b/src/layouts/DefaultLayout/components/AppSidebar/index.js @@ -45,77 +45,77 @@ const AppSidebar = () => { label: __('Bandi osservati', 'gepafin'), icon: 'pi pi-star', href: '/bandi-osservati', - id: 13, + id: 5, enable: intersection(permissions, ['VIEW_CALLS']).length }, { label: __('Gestione domande', 'gepafin'), icon: 'pi pi-file', href: '/domande', - id: 5, + id: 6, enable: intersection(permissions, ['VIEW_USERS', 'MANAGE_USERS']).length }, { label: __('Domande da valutare', 'gepafin'), icon: 'pi pi-calendar-clock', href: '/domande', - id: 6, + id: 7, enable: intersection(permissions, ['EVALUATE_APPLICATIONS']).length }, { label: __('Archivio domande', 'gepafin'), icon: 'pi pi-briefcase', href: '/domande', - id: 7, + id: 8, enable: intersection(permissions, ['APPLY_CALLS']).length }, { label: __('Archivio domande', 'gepafin'), icon: 'pi pi-briefcase', href: '/domande-archivio', - id: 5, + id: 9, enable: intersection(permissions, ['VIEW_USERS', 'MANAGE_USERS']).length }, { label: __('Archivio domande', 'gepafin'), icon: 'pi pi-briefcase', href: '/domande-archivio', - id: 6, + id: 10, enable: intersection(permissions, ['EVALUATE_APPLICATIONS']).length }, { label: __('Soccorso istruttorio', 'gepafin'), icon: , href: '/soccorso-istruttorio', - id: 8, + id: 11, enable: intersection(permissions, ['EVALUATE_APPLICATIONS']).length }, { label: __('Gestione utenti', 'gepafin'), icon: 'pi pi-users', href: '/utenti', - id: 9, + id: 12, enable: intersection(permissions, ['VIEW_USERS', 'MANAGE_USERS']).length }, { label: __('Configurazione', 'gepafin'), icon: 'pi pi-cog', //href: '/configurazione', - id: 10, + id: 13, enable: false }, { label: __('Report e Analisi', 'gepafin'), icon: 'pi pi-chart-bar', //href: '/stats', - id: 11, + id: 14, enable: false }, { label: __('Log di Sistema', 'gepafin'), icon: 'pi pi-receipt', clickFn: () => {}, - id: 12, + id: 15, enable: false } ] diff --git a/src/pages/Bandi/components/AllBandiTable/index.js b/src/pages/Bandi/components/AllBandiTable/index.js index 9bbc70f..14e457b 100644 --- a/src/pages/Bandi/components/AllBandiTable/index.js +++ b/src/pages/Bandi/components/AllBandiTable/index.js @@ -121,7 +121,7 @@ const AllBandiTable = () => { return(
- {
{
{ return (
- { const [filters, setFilters] = useState(null); const [localAsyncRequest, setLocalAsyncRequest] = useState(false); const [, setStatuses] = useState([]); + /*const [totalRecordsNum, setTotalRecordsNum] = useState(0); + const [perPageNum, setPerPageNum] = useState(0); + const getPaginationQuery = () => { + return { + "globalFilters": { + "page": 1, + "limit": 5, + "sortBy": { + "columnName": "ID", + "sortDesc": true + } + } + } + } + + const onPageChange = (e) => { + console.log('onPageChange', e) + }*/ useEffect(() => { setLocalAsyncRequest(true); BandoService.getBandi(getCallback, errGetCallbacks); + //const paginationQuery = getPaginationQuery(); + //BandoService.getBandiPaginated(paginationQuery, getCallback, errGetCallbacks); }, []); const getCallback = (data) => { if (data.status === 'SUCCESS') { + /*const { body, totalRecords, currentPage, totalPages, pageSize } = data.data; + setTotalRecordsNum(totalRecords); + setPerPageNum(pageSize); + const newItems = body.filter(o => o.status === 'PUBLISH'); + setItems(getFormattedBandiData(newItems)); + setStatuses(uniq(body.map(o => o.status)));*/ const newItems = data.data.filter(o => o.status === 'PUBLISH'); setItems(getFormattedBandiData(newItems)); - setStatuses(uniq(data.data.map(o => o.status))) + setStatuses(uniq(data.data.map(o => o.status))); initFilters(); } setLocalAsyncRequest(false); @@ -106,7 +132,10 @@ const LatestBandiTable = () => { return(
- { return(
- { return (
- { return (
- { return(
- { return (
- { const [filters, setFilters] = useState(null); const [localAsyncRequest, setLocalAsyncRequest] = useState(false); const [statuses, setStatuses] = useState([]); - const location = useLocation(); useEffect(() => { setLocalAsyncRequest(true); @@ -141,7 +140,7 @@ const AllDomandeArchiveTable = ({ updaterString = '' }) => { return (
- { return (
- { return(
- { return (
- { + NetworkService.post(`${API_BASE_URL}/call/pagination`, body, callback, errCallback, queryParams); + }; + static getBando = (id, callback, errCallback, queryParams) => { NetworkService.get(`${API_BASE_URL}/call/${id}`, callback, errCallback, queryParams); }; diff --git a/src/service/notification-service.js b/src/service/notification-service.js index c3457b2..25b77f6 100644 --- a/src/service/notification-service.js +++ b/src/service/notification-service.js @@ -8,6 +8,14 @@ export default class NotificationService { NetworkService.get(`${API_BASE_URL}/notification/user/${id}`, callback, errCallback, queryParams); }; + static getNotificationsByCompanyId = (id, companyId, callback, errCallback, queryParams) => { + NetworkService.get(`${API_BASE_URL}/notification/user/${id}/company/${companyId}/notifications`, callback, errCallback, queryParams); + }; + + static getNotificationsPagination = (id, body, callback, errCallback, queryParams) => { + NetworkService.post(`${API_BASE_URL}/notification/user/${id}/pagination`, body, callback, errCallback, queryParams); + }; + static notificationMakeRead = (id, callback, errCallback) => { NetworkService.put(`${API_BASE_URL}/notification/${id}`, {}, callback, errCallback, [ ['status', 'READ'] From f4aef029414edb580ea320c9775e0291282ada26 Mon Sep 17 00:00:00 2001 From: Vitalii Kiiko Date: Wed, 15 Jan 2025 16:15:43 +0100 Subject: [PATCH 02/27] - saving progress; --- src/components/ChartDomandePerBando/index.js | 8 +- src/components/ChartStatoDomande/index.js | 2 +- src/pages/UserActivity/index.js | 94 ++++++++++++++------ src/service/notification-service.js | 4 +- src/service/user-action-service.js | 14 +++ 5 files changed, 89 insertions(+), 33 deletions(-) create mode 100644 src/service/user-action-service.js diff --git a/src/components/ChartDomandePerBando/index.js b/src/components/ChartDomandePerBando/index.js index 700e6a7..b13d369 100644 --- a/src/components/ChartDomandePerBando/index.js +++ b/src/components/ChartDomandePerBando/index.js @@ -22,7 +22,10 @@ const ChartDomandePerBando = ({ title, data = [] }) => {

{label}

- {__('Domande', 'gepafin')}: {payload[0].value} + {__('In bozza', 'gepafin')}: {payload[0].value} +

+

+ {__('Inviate', 'gepafin')}: {payload[1].value}

); @@ -50,7 +53,8 @@ const ChartDomandePerBando = ({ title, data = [] }) => { }/> - + +
: null} diff --git a/src/components/ChartStatoDomande/index.js b/src/components/ChartStatoDomande/index.js index 856fd6c..e5a9ddf 100644 --- a/src/components/ChartStatoDomande/index.js +++ b/src/components/ChartStatoDomande/index.js @@ -38,7 +38,7 @@ const ChartStatoDomande = ({ title, data = [] }) => { label={({ percent }) => `${(percent * 100).toFixed(0)}%`} outerRadius={120} fill="#8884d8" - dataKey="numberOfApplications" + dataKey="numberOfSubmitedApplications" nameKey="status" > {data.map((entry, index) => ( diff --git a/src/pages/UserActivity/index.js b/src/pages/UserActivity/index.js index 8e93f5e..699cf38 100644 --- a/src/pages/UserActivity/index.js +++ b/src/pages/UserActivity/index.js @@ -15,6 +15,7 @@ import getDateFromISOstring from '../../helpers/getDateFromISOstring'; import { Button } from 'primereact/button'; import { Toast } from 'primereact/toast'; import { Dropdown } from 'primereact/dropdown'; +import UserActionService from '../../service/user-action-service'; const UserActivity = () => { @@ -25,6 +26,8 @@ const UserActivity = () => { const [user, setUser] = useState({}); const [roles, setRoles] = useState([]); const [chosenRole, setChosenRole] = useState(0); + const [actionsContext, setActionsContext] = useState([]); + const [userActions, setUserActions] = useState({}); const goBack = () => { navigate(`/utenti`); @@ -56,13 +59,13 @@ const UserActivity = () => { } const getStatValue = (key, fallback = 0) => { - return pathOr(fallback, [key], {}); + return pathOr(fallback, [key], userActions); } const handleRoleUpdate = () => { if (user.role?.id !== chosenRole) { setLoading(true); - UserService.updateUser(user.id, {roleId: chosenRole}, updateRoleCallback, errUpdateRoleCallback) + UserService.updateUser(user.id, { roleId: chosenRole }, updateRoleCallback, errUpdateRoleCallback) } } @@ -78,11 +81,37 @@ const UserActivity = () => { setLoading(false); } + const getActionsContextCallback = (resp) => { + if (resp.status === 'SUCCESS') { + setActionsContext(resp.data) + } + setLoading(false); + } + + const errGetActionsContextCallback = (resp) => { + set404FromErrorResponse(resp); + setLoading(false); + } + + const getUserActionsCallback = (resp) => { + if (resp.status === 'SUCCESS') { + setUserActions(resp.data) + } + setLoading(false); + } + + const errGetUserActionsCallback = (resp) => { + set404FromErrorResponse(resp); + setLoading(false); + } + useEffect(() => { if (id && !isEmpty(id)) { setLoading(true); UserService.getUser(id, getUserCallback, errGetUserCallback); UserService.getRoles(getRolesCallback, errGetRolesCallback); + UserActionService.getActionContext(id, getActionsContextCallback, errGetActionsContextCallback); + UserActionService.getUserActions(id, getUserActionsCallback, errGetUserActionsCallback); } }, [id]) @@ -143,7 +172,10 @@ const UserActivity = () => { disabled={isEmpty(roles) || loading} value={chosenRole} onChange={(e) => setChosenRole(e.value)} - options={roles.filter(o => [3, 5].includes(o.id)).map(o => ({ label: o.roleName, value: o.id }))} + options={roles.filter(o => [3, 5].includes(o.id)).map(o => ({ + label: o.roleName, + value: o.id + }))} optionLabel="label" placeholder={__('Seleziona ruolo', 'gepafin')}/>
+ ); + }; + + const dateBodyTemplate = (rowData) => { + return formatDate(rowData.createdDate); + }; + + const header = renderHeader(); + + return ( +
+ setFilters(e.filters)}> + + + + + +
+ ) +} + +export default UserActivityTable; diff --git a/src/pages/UserActivity/index.js b/src/pages/UserActivity/index.js index 699cf38..a29a6b7 100644 --- a/src/pages/UserActivity/index.js +++ b/src/pages/UserActivity/index.js @@ -16,6 +16,7 @@ import { Button } from 'primereact/button'; import { Toast } from 'primereact/toast'; import { Dropdown } from 'primereact/dropdown'; import UserActionService from '../../service/user-action-service'; +import UserActivityTable from './components/UserActivityTable'; const UserActivity = () => { @@ -28,6 +29,9 @@ const UserActivity = () => { const [chosenRole, setChosenRole] = useState(0); const [actionsContext, setActionsContext] = useState([]); const [userActions, setUserActions] = useState({}); + const [chosenPeriod, setChosenPeriod] = useState(''); + const [chosenActivity, setChosenActivity] = useState(''); + const goBack = () => { navigate(`/utenti`); @@ -105,6 +109,19 @@ const UserActivity = () => { setLoading(false); } + const doFilterUserActivity = () => { + let queryParams = []; + + if (!isEmpty(chosenPeriod)) { + queryParams.push(['timeFilter', chosenPeriod]) + } + if (!isEmpty(chosenActivity)) { + queryParams.push(['actionContext', chosenActivity]) + } + + UserActionService.getUserActions(id, getUserActionsCallback, errGetUserActionsCallback, queryParams); + } + useEffect(() => { if (id && !isEmpty(id)) { setLoading(true); @@ -219,9 +236,53 @@ const UserActivity = () => {
: null} +
+ {!isEmpty(userActions) ?
-

{__('Statistiche attività', 'gepafin')}

+

{__('Filtri attività', 'gepafin')}

+
+
+ + setChosenPeriod(e.value)} + options={[ + { value: 'LAST_WEEK', label: __('Ultima settimana', 'gepafin') }, + { value: 'LAST_QUARTER', label: __('Ultimo trimestre', 'gepafin') }, + { value: 'LAST_SEMESTER', label: __('Ultimo semestre', 'gepafin') }, + { value: 'LAST_YEAR', label: __('Ultimo anno', 'gepafin') } + ]} + optionLabel="label"/> +
+ {!isEmpty(actionsContext) + ?
+ + setChosenActivity(e.value)} + options={actionsContext.map(o => ({ + value: o.actionContext, + label: o.description + }))} + optionLabel="label"/> +
: null} +
+
+
+
: null} + +
+ + {!isEmpty(userActions) + ?
+

{__('Attività dettagliate', 'gepafin')}

+
: null}
) From 57995e964671ef49d11e736d5bb0018497cfe401 Mon Sep 17 00:00:00 2001 From: Vitalii Kiiko Date: Fri, 17 Jan 2025 12:16:11 +0100 Subject: [PATCH 04/27] - removed hardcoded call id 13 forced expiration; --- src/pages/BandoApplication/index.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/pages/BandoApplication/index.js b/src/pages/BandoApplication/index.js index 2d7d57c..0e5846d 100644 --- a/src/pages/BandoApplication/index.js +++ b/src/pages/BandoApplication/index.js @@ -301,9 +301,6 @@ const BandoApplication = () => { setActiveStep(data.data.currentStep); const isCallExpired = isDateTimeInPast(data.data.callEndDate, data.data.callEndTime); setIsExpired(isCallExpired); - if (data.data.callId === 13) { - setIsExpired(true); - } const chosenCompanyId = data.data.companyId; const companies = storeGet.main.companies(); From 61763a961b4fbd6373d0fd1b05047e1ac26b67ec Mon Sep 17 00:00:00 2001 From: Vitalii Kiiko Date: Fri, 17 Jan 2025 15:46:29 +0100 Subject: [PATCH 05/27] - updated code, added new settings; --- src/assets/scss/components/formBuilder.scss | 5 + src/assets/scss/components/misc.scss | 6 +- .../components/BuilderElement/index.js | 28 ++- .../components/ElementSetting/index.js | 8 + .../components/ElementSettingChips/index.js | 43 +++++ .../ElementSettingTableColumns-old/index.js | 159 ------------------ .../BuilderElementSettings/index.js | 16 +- src/pages/BandoFormsEdit/index.js | 6 +- src/tempData.js | 10 +- 9 files changed, 114 insertions(+), 167 deletions(-) create mode 100644 src/pages/BandoFormsEdit/components/BuilderElementSettings/components/ElementSettingChips/index.js delete mode 100644 src/pages/BandoFormsEdit/components/BuilderElementSettings/components/ElementSettingTableColumns-old/index.js diff --git a/src/assets/scss/components/formBuilder.scss b/src/assets/scss/components/formBuilder.scss index b07f322..9382f37 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 { diff --git a/src/assets/scss/components/misc.scss b/src/assets/scss/components/misc.scss index 8f89d4f..9f59541 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%; } diff --git a/src/pages/BandoFormsEdit/components/BuilderElement/index.js b/src/pages/BandoFormsEdit/components/BuilderElement/index.js index 8a7b86e..9f5e0d0 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 }) => {
:
- +
+ + {name === 'numberinput' + ? : 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..8a5d0c8 100644 --- a/src/pages/BandoFormsEdit/components/BuilderElementSettings/components/ElementSetting/index.js +++ b/src/pages/BandoFormsEdit/components/BuilderElementSettings/components/ElementSetting/index.js @@ -11,6 +11,7 @@ import { Editor } from 'primereact/editor'; import { mimeTypes } from '../../../../../../configData'; import ElementSettingTableColumns from '../ElementSettingTableColumns'; import { InputSwitch } from 'primereact/inputswitch'; +import ElementSettingChips from '../ElementSettingChips'; const ElementSetting = ({ setting, changeFn, updateDataFn, bandoStatus }) => { @@ -24,6 +25,8 @@ const ElementSetting = ({ setting, changeFn, updateDataFn, bandoStatus }) => { mime: __('Tipo di file', 'gepafin'), text: __('Testo formattato', 'gepafin'), table_columns: '', + variable: __('Variable (only letters and "_")', 'gepafin'), + formula: __('Auto calculation formula', 'gepafin') } const renderHeader = () => { @@ -85,6 +88,11 @@ const ElementSetting = ({ setting, changeFn, updateDataFn, bandoStatus }) => { return changeFn(e.value, setting.name)}/> + } else if (['variable'].includes(setting.name)) { + return changeFn(value, setting.name)} + value={setting.value}/> } else { return { + const [lastTyped, setLastTyped] = useState([]) + + const isValidValue = (newVal) => { + const validationRegex = /^[a-zA-Z_]+$/; + return validationRegex.test(newVal); + }; + + const handleAdd = (e) => { + const newValue = 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/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/index.js b/src/pages/BandoFormsEdit/components/BuilderElementSettings/index.js index 3e4f66a..0c1eed9 100644 --- a/src/pages/BandoFormsEdit/components/BuilderElementSettings/index.js +++ b/src/pages/BandoFormsEdit/components/BuilderElementSettings/index.js @@ -160,7 +160,9 @@ const BuilderElementSettings = ({ closeSettingsFn, bandoStatus }) => { {settings - ? settings.map((o) => !['variable', 'formula'].includes(o.name)) + .map((o) => { placeholder={__('Scegli', 'gepafin')}/>
+ + {settings + ? settings + .filter(o => ['variable', 'formula'].includes(o.name)) + .map((o) => ) + : null} +
: { 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); @@ -162,18 +161,18 @@ const BandoFlowEdit = () => { const shoudDisableSaving = useCallback(() => { const nonEmptyFlowItems = flowStructure.flowData.filter(o => isEmpty(o.chosenField)).filter(o => !isEmpty(o.chosenValue)); - /*if (flowForms.length > 2) { - console.log('disable BTN:', nonEmptyFlowItems.length !== flowForms.length - 2, isEmpty(flowEdges), 'PUBLISH' === bandoStatus, - isEmpty(initialForm), isEmpty(finalForm)); + /*if (forms.length > 2) { + console.log('disable BTN:', nonEmptyFlowItems.length !== forms.length - 2, isEmpty(flowStructure.flowEdges), 'PUBLISH' === bandoStatus, + isEmpty(flowStructure.initialForm), isEmpty(flowStructure.finalForm)); } else { - console.log('disable BTN:', nonEmptyFlowItems.length !== 1, isEmpty(flowEdges), 'PUBLISH' === bandoStatus, - isEmpty(initialForm), isEmpty(finalForm)); + console.log('disable BTN (2 forms):', isEmpty(flowStructure.flowEdges), 'PUBLISH' === bandoStatus, + isEmpty(flowStructure.initialForm), isEmpty(flowStructure.finalForm)); }*/ return forms.length > 2 ? nonEmptyFlowItems.length !== forms.length - 2 || isEmpty(flowStructure.flowEdges) || 'PUBLISH' === bandoStatus || isEmpty(flowStructure.initialForm) || isEmpty(flowStructure.finalForm) - : nonEmptyFlowItems.length !== 1 || isEmpty(flowStructure.flowEdges) || 'PUBLISH' === bandoStatus + : isEmpty(flowStructure.flowEdges) || 'PUBLISH' === bandoStatus || isEmpty(flowStructure.initialForm) || isEmpty(flowStructure.finalForm); }, [flowStructure, forms]); diff --git a/src/pages/BandoFormsEdit/components/BuilderElement/index.js b/src/pages/BandoFormsEdit/components/BuilderElement/index.js index 9f5e0d0..71c0324 100644 --- a/src/pages/BandoFormsEdit/components/BuilderElement/index.js +++ b/src/pages/BandoFormsEdit/components/BuilderElement/index.js @@ -124,7 +124,7 @@ const BuilderElement = ({ id, name, label, index, bandoStatus }) => {
- {name === 'numberinput' + {['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 8a5d0c8..cb50706 100644 --- a/src/pages/BandoFormsEdit/components/BuilderElementSettings/components/ElementSetting/index.js +++ b/src/pages/BandoFormsEdit/components/BuilderElementSettings/components/ElementSetting/index.js @@ -2,19 +2,23 @@ import React from 'react'; import { __ } from '@wordpress/i18n'; import { is } from 'ramda'; +// 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 settingLabels = { label: __('Label', 'gepafin'), placeholder: __('Segnaposto', 'gepafin'), @@ -25,10 +29,15 @@ 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 = () => { return ( @@ -84,6 +93,12 @@ const ElementSetting = ({ setting, changeFn, updateDataFn, bandoStatus }) => { name={setting.name} bandoStatus={bandoStatus} setDataFn={updateDataFn}/> + } else if (setting.name === 'criteria_table_columns') { + return } else if (['isRequestedAmount', 'isDelegation'].includes(setting.name)) { return { return
{getProperField(setting)} + {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 index 9b44a7e..846dcc5 100644 --- a/src/pages/BandoFormsEdit/components/BuilderElementSettings/components/ElementSettingChips/index.js +++ b/src/pages/BandoFormsEdit/components/BuilderElementSettings/components/ElementSettingChips/index.js @@ -2,8 +2,9 @@ import React, { useState } from 'react'; // components import { Chips } from 'primereact/chips'; +import { isEmpty } from 'ramda'; -const ElementSettingChips = ({ restrictedValues = [], changeFn, value }) => { +const ElementSettingChips = ({ restrictedValues = [], changeFn, value = [] }) => { const [lastTyped, setLastTyped] = useState([]) const isValidValue = (newVal) => { @@ -12,7 +13,7 @@ const ElementSettingChips = ({ restrictedValues = [], changeFn, value }) => { }; const handleAdd = (e) => { - const newValue = e.value[e.value.length - 1]; + const newValue = isEmpty(e.value) ? '' : e.value[e.value.length - 1]; if (restrictedValues.includes(newValue)) { changeFn([]); 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..0053d76 --- /dev/null +++ b/src/pages/BandoFormsEdit/components/BuilderElementSettings/components/ElementSettingCriteriaTableColumns/index.js @@ -0,0 +1,326 @@ +import React, { useCallback, useEffect, useState } from 'react'; +import { __, sprintf } from '@wordpress/i18n'; +import { wrap } from 'object-path-immutable'; +import { isEmpty, 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'; +import { Dropdown } from 'primereact/dropdown'; +import { Accordion, AccordionTab } from 'primereact/accordion'; + +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 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/index.js b/src/pages/BandoFormsEdit/components/BuilderElementSettings/components/ElementSettingTableColumns/index.js index 1806e6d..30eb834 100644 --- a/src/pages/BandoFormsEdit/components/BuilderElementSettings/components/ElementSettingTableColumns/index.js +++ b/src/pages/BandoFormsEdit/components/BuilderElementSettings/components/ElementSettingTableColumns/index.js @@ -158,7 +158,7 @@ const ElementSettingTableColumns = ({
onTypeChange(e.value, i)} options={[ @@ -168,6 +168,7 @@ const ElementSettingTableColumns = ({
@@ -212,6 +214,7 @@ const ElementSettingTableColumns = ({ {item.enableFormula ?
onLastRowFormulaChange(e.value, i)} options={[ @@ -232,6 +235,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 0c1eed9..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(); @@ -163,11 +147,11 @@ const BuilderElementSettings = ({ closeSettingsFn, bandoStatus }) => { ? settings .filter(o => !['variable', 'formula'].includes(o.name)) .map((o) => ) + key={o.name} + setting={o} + bandoStatus={bandoStatus} + changeFn={onChange} + updateDataFn={onUpdateOptions}/>) : null} {!isNil(dynamicDataOptions[activeElementData.name]) ?
@@ -250,18 +234,21 @@ const BuilderElementSettings = ({ closeSettingsFn, bandoStatus }) => { placeholder={__('Scegli', 'gepafin')}/>
- - {settings - ? settings - .filter(o => ['variable', 'formula'].includes(o.name)) - .map((o) => ) - : null} - + {settings + && settings + .filter(o => ['variable', 'formula'].includes(o.name)).length > 0 + ? + {settings + ? settings + .filter(o => ['variable', 'formula'].includes(o.name)) + .map((o) => ) + : null} + : null}
: Date: Wed, 22 Jan 2025 16:08:00 +0100 Subject: [PATCH 07/27] - added amendment stats; - improved displaying messages during application validation; --- src/pages/BandoApplication/index.js | 35 ++++++--- src/pages/BandoFlowEdit/index.js | 12 +-- .../DomandaEditInstructorManager/index.js | 27 ++++--- src/pages/DomandaEditPreInstructor/index.js | 13 ++-- src/pages/Domande/index.js | 2 +- .../SoccorsoIstruttorioPreInstructor/index.js | 74 ++++++++++++++++++- src/service/dashboard-service.js | 4 + 7 files changed, 128 insertions(+), 39 deletions(-) diff --git a/src/pages/BandoApplication/index.js b/src/pages/BandoApplication/index.js index 0e5846d..f46dcf4 100644 --- a/src/pages/BandoApplication/index.js +++ b/src/pages/BandoApplication/index.js @@ -165,19 +165,32 @@ const BandoApplication = () => { const validateApplicationCallback = (data) => { if (data.status === 'SUCCESS') { if (data.data.status) { - setApplicationStatus(data.data.status); // ask why not 'applicationStatus'? + setApplicationStatus(data.data.status); } } storeSet.main.unsetAsyncRequest(); } const errValidateApplicationCallback = (data) => { - if (toast.current) { - toast.current.show({ - severity: 'error', - summary: '', - detail: data.message - }); + if (data.status === 'VALIDATION_ERROR') { + if (formMsgs.current) { + formMsgs.current.show([ + { + id: '99', + sticky: true, severity: 'error', summary: '', + detail: data.data.join(' '), + closable: true + } + ]); + } + } else { + if (toast.current) { + toast.current.show({ + severity: 'error', + summary: '', + detail: data.message + }); + } } storeSet.main.unsetAsyncRequest(); } @@ -260,7 +273,7 @@ const BandoApplication = () => { { id: '99', sticky: true, severity: 'error', summary: '', - detail: data.data.join(', '), + detail: data.data.join(' '), closable: true } ]); @@ -420,7 +433,9 @@ const BandoApplication = () => { icon="pi pi-arrow-right" iconPos="right"/> : null}
: null} - {'DRAFT' !== applicationStatus + {'AWAITING' === applicationStatus ?
: null} - {'DRAFT' !== applicationStatus + {['AWAITING', 'READY'].includes(applicationStatus) ?
diff --git a/src/pages/SoccorsoIstruttorioPreInstructor/index.js b/src/pages/SoccorsoIstruttorioPreInstructor/index.js index c254df7..6a92b97 100644 --- a/src/pages/SoccorsoIstruttorioPreInstructor/index.js +++ b/src/pages/SoccorsoIstruttorioPreInstructor/index.js @@ -1,10 +1,31 @@ -import React from 'react'; +import React, { useEffect, useState } from 'react'; import { __ } from '@wordpress/i18n'; // components import PreInstructorSoccorsiTable from './components/PreInstructorSoccorsiTable'; +import DashboardService from '../../service/dashboard-service'; +import { pathOr } from 'ramda'; +import NumberFlow from '@number-flow/react'; const SoccorsoIstruttorioPreInstructor = () => { + const [mainStats, setMainStats] = useState({}); + + const getStats = (data) => { + if (data.status === 'SUCCESS') { + setMainStats(data.data); + } + } + + const errGetStats = () => {} + + const getStatValue = (key, fallback = '') => { + return pathOr(fallback, [key], mainStats); + } + + useEffect(() => { + DashboardService.getAmendmentsStats(getStats, errGetStats); + }, []); + return(
@@ -16,6 +37,57 @@ const SoccorsoIstruttorioPreInstructor = () => {
+ +
+ +
+

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

+
+
+ {__('Totale richieste', 'gepafin')} + +
+
+ {__('In attesa risposta', 'gepafin')} + +
+
+ {__('Risposte ricevute', 'gepafin')} + +
+
+ {__('Tempo medio di risposta', 'gepafin')} + +
+
+ {__('Scadute', 'gepafin')} + +
+
+ {__('Richieste in scadenza (48h)', 'gepafin')} + +
+
+
) } diff --git a/src/service/dashboard-service.js b/src/service/dashboard-service.js index 87d7572..6951e9c 100644 --- a/src/service/dashboard-service.js +++ b/src/service/dashboard-service.js @@ -12,6 +12,10 @@ export default class DashboardService { NetworkService.get(`${API_BASE_URL}/dashboard/application`, callback, errCallback); }; + static getAmendmentsStats = (callback, errCallback) => { + NetworkService.get(`${API_BASE_URL}/dashboard/amendment`, callback, errCallback); + }; + static getBeneficiaryStatsForCompany = (id, callback, errCallback) => { NetworkService.get(`${API_BASE_URL}/dashboard/beneficiary/company/${id}`, callback, errCallback); }; From e99a9b20582043522779fd610ad12231e22ecbb5 Mon Sep 17 00:00:00 2001 From: Vitalii Kiiko Date: Thu, 23 Jan 2025 12:54:27 +0100 Subject: [PATCH 08/27] - saving progress; --- src/assets/scss/components/appForm.scss | 2 +- .../components/LastRowCell/index.js | 15 ++----- .../CriteriaTable/RenderTable/index.js | 44 ++++++++++++------- .../components/CriteriaTable/index.js | 32 +++++++------- .../components/LastRowCell/index.js | 4 +- .../components/Table/RenderTable/index.js | 14 +++--- .../FormField/components/Table/index.js | 6 +-- 7 files changed, 62 insertions(+), 55 deletions(-) diff --git a/src/assets/scss/components/appForm.scss b/src/assets/scss/components/appForm.scss index 405d28e..2f5ef85 100644 --- a/src/assets/scss/components/appForm.scss +++ b/src/assets/scss/components/appForm.scss @@ -88,7 +88,6 @@ color: var(--global-textColor); font-size: 15px; text-align: left; - text-align: start; } th { @@ -97,6 +96,7 @@ } td { + min-width: 120px; input { width: 100%; padding: 3px 5px; diff --git a/src/components/FormField/components/CriteriaTable/RenderTable/components/LastRowCell/index.js b/src/components/FormField/components/CriteriaTable/RenderTable/components/LastRowCell/index.js index 3a35e77..c9e12a0 100644 --- a/src/components/FormField/components/CriteriaTable/RenderTable/components/LastRowCell/index.js +++ b/src/components/FormField/components/CriteriaTable/RenderTable/components/LastRowCell/index.js @@ -1,18 +1,11 @@ -import { head, isEmpty, isNil, sum } from 'ramda'; +import { head, isNil, pathOr } from 'ramda'; -const LastRowCell = ({columnId, lastRows, columnMeta = {}, getColumnDataFn}) => { - const cellData = head(lastRows.filter(o => !isNil(o[columnId]))); +const LastRowCell = ({columnId, lastRowCfg, columnMeta = {}, tableValue = []}) => { + const cellData = head(lastRowCfg.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; - } + cellValue = pathOr(0, ['total'], tableValue); } return {cellValue}; diff --git a/src/components/FormField/components/CriteriaTable/RenderTable/index.js b/src/components/FormField/components/CriteriaTable/RenderTable/index.js index 604a453..c15d7c8 100644 --- a/src/components/FormField/components/CriteriaTable/RenderTable/index.js +++ b/src/components/FormField/components/CriteriaTable/RenderTable/index.js @@ -1,16 +1,17 @@ import React from 'react'; import { flexRender, getCoreRowModel, useReactTable } from '@tanstack/react-table'; import { wrap } from 'object-path-immutable'; -import { isEmpty } from 'ramda'; +import { head, isEmpty, isNil, pathOr, sum } from 'ramda'; // components import DefaultCell from './components/DefaultCell'; import LastRowCell from './components/LastRowCell'; -const RenderTable = ({ data, columns, lastRow, setRowsFn, disabled }) => { +const RenderTable = ({ tableValue = {}, columnsCfg, lastRowCfg, setTableValueFn, disabled }) => { + const rows = pathOr([], ['rows'], tableValue) const table = useReactTable({ - data, - columns, + data: rows, + columns: columnsCfg, defaultColumn: { cell: DefaultCell }, @@ -18,17 +19,30 @@ const RenderTable = ({ data, columns, lastRow, setRowsFn, disabled }) => { meta: { disabled, updateData: (rowIndex, columnId, value) => { - const newRowsData = wrap(data).set([rowIndex, columnId], value).value(); - setRowsFn(newRowsData); + 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 = sum(getAllRowsValues); + } else { + total = 0; + } + } + + newRowsData = wrap(newRowsData).set(['total'], total).value(); + setTableValueFn(newRowsData); }, } }); - const getColumnData = (columnId) => { - const rows = table.getRowModel().rows; - return rows.map(row => row.getValue(columnId)); - }; - return ( @@ -68,14 +82,14 @@ const RenderTable = ({ data, columns, lastRow, setRowsFn, disabled }) => { ); })} - {!isEmpty(lastRow) + {!isEmpty(lastRowCfg) ? - {columns.map((o) => )} + lastRowCfg={lastRowCfg} + tableValue={tableValue}/>)} : null} diff --git a/src/components/FormField/components/CriteriaTable/index.js b/src/components/FormField/components/CriteriaTable/index.js index e1d65f2..3801617 100644 --- a/src/components/FormField/components/CriteriaTable/index.js +++ b/src/components/FormField/components/CriteriaTable/index.js @@ -27,12 +27,12 @@ const Table = ({ const [rowsCfg, setRowsCfg] = useState([]); const [columns, setColumns] = useState([]); const [lastRow, setLastRow] = useState([]); - const [rows, setRows] = useState(null); + const [tableValue, setTableValue] = useState(null); - const updateRows = useCallback((data) => { - setRows(data); + const updateValue = useCallback((data) => { + setTableValue(data); setDataFn(fieldName, data, { shouldValidate: true }); - }, [rows, defaultValue]); + }, [tableValue, defaultValue]); const properConfig = (config) => { let newConfig = klona(config); @@ -71,7 +71,7 @@ const Table = ({ }, [columnsCfg, disabled]); useEffect(() => { - setRows(rowsCfg); + setTableValue(rowsCfg); }, [rowsCfg]); useEffect(() => { @@ -84,7 +84,7 @@ const Table = ({ let rowsData = pathOr([obj], ['rowsData'], tableColumns); rowsData = isEmpty(rowsData) ? [obj] : rowsData; setColumnsCfg(stateFieldData); - setRowsCfg(rowsData); + setRowsCfg({rows: rowsData, total: 0}); let lastRowData = stateFieldData.reduce((acc, cur) => { const value = cur.enableFormula ? cur.lastRowFormula : (cur.lastRowText ? cur.lastRowText : ''); @@ -103,29 +103,29 @@ const Table = ({ }, [tableColumns]); useEffect(() => { - if (!equal(rows, defaultValue)) { - setRows(defaultValue); + if (!equal(tableValue, defaultValue)) { + setTableValue(defaultValue); } }, [defaultValue]); useEffect(() => { - setRows(defaultValue); + setTableValue(defaultValue); register(fieldName, properConfig(config)); }, []); - console.log('rows', rows, lastRow, tableColumns) return ( <> - {rows ? : null} + {tableValue + ? : null} ) } 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..84bd57b 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,7 @@ import { head, isEmpty, isNil, sum } from 'ramda'; -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) { diff --git a/src/components/FormField/components/Table/RenderTable/index.js b/src/components/FormField/components/Table/RenderTable/index.js index 604a453..43cf9ac 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,13 +68,13 @@ const RenderTable = ({ data, columns, lastRow, setRowsFn, disabled }) => { ); })} - {!isEmpty(lastRow) + {!isEmpty(lastRowCfg) ? - {columns.map((o) => )} : null} diff --git a/src/components/FormField/components/Table/index.js b/src/components/FormField/components/Table/index.js index 8ebd19b..c925b08 100644 --- a/src/components/FormField/components/Table/index.js +++ b/src/components/FormField/components/Table/index.js @@ -170,9 +170,9 @@ const Table = ({ ? * : null} {rows ? : null} {!isEmpty(columns) && !shouldDisableNewRows From df99a3e77d5bb6e087d3369503a021f8fd43d746 Mon Sep 17 00:00:00 2001 From: Vitalii Kiiko Date: Fri, 24 Jan 2025 12:16:09 +0100 Subject: [PATCH 09/27] - added form fields calculation and new table calculationl tested in preview; --- src/assets/scss/components/appForm.scss | 11 +-- src/assets/scss/components/formBuilder.scss | 33 ++++++-- .../components/DefaultCell/index.js | 15 ++-- .../components/NumericFormulaCell/index.js | 26 +++---- .../CriteriaTable/RenderTable/index.js | 6 +- .../components/CriteriaTable/index.js | 8 +- .../components/DefaultCell/index.js | 15 ++-- .../components/NumericFormulaCell/index.js | 26 +++---- .../components/Table/RenderTable/index.js | 6 +- .../FormField/components/Table/index.js | 2 +- src/helpers/keepKeys.js | 3 + src/helpers/removeKey.js | 3 + .../components/ElementSetting/index.js | 34 ++++++++- .../index.js | 11 ++- .../ElementSettingTableColumns/index.js | 9 ++- src/pages/BandoFormsPreview/index.js | 75 ++++++++----------- src/pages/ResetPassword/index.js | 4 +- 17 files changed, 152 insertions(+), 135 deletions(-) create mode 100644 src/helpers/keepKeys.js create mode 100644 src/helpers/removeKey.js diff --git a/src/assets/scss/components/appForm.scss b/src/assets/scss/components/appForm.scss index 2f5ef85..dad056e 100644 --- a/src/assets/scss/components/appForm.scss +++ b/src/assets/scss/components/appForm.scss @@ -59,11 +59,8 @@ &.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; @@ -99,14 +96,12 @@ 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 178cd4a..db9b8ad 100644 --- a/src/assets/scss/components/formBuilder.scss +++ b/src/assets/scss/components/formBuilder.scss @@ -181,7 +181,7 @@ gap: 0.5rem; } -.formElementSettings__fieldDescription { +.formElementSettings__fieldDescription, .formElementSettings__fieldVarsList { padding: 15px; background-color: #ffe0c5; border: 1px solid #e6a973; @@ -191,14 +191,31 @@ 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; - } + 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; } } diff --git a/src/components/FormField/components/CriteriaTable/RenderTable/components/DefaultCell/index.js b/src/components/FormField/components/CriteriaTable/RenderTable/components/DefaultCell/index.js index 6351f92..0df7324 100644 --- a/src/components/FormField/components/CriteriaTable/RenderTable/components/DefaultCell/index.js +++ b/src/components/FormField/components/CriteriaTable/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/CriteriaTable/RenderTable/components/NumericFormulaCell/index.js b/src/components/FormField/components/CriteriaTable/RenderTable/components/NumericFormulaCell/index.js index 58a6f25..75f3b3a 100644 --- a/src/components/FormField/components/CriteriaTable/RenderTable/components/NumericFormulaCell/index.js +++ b/src/components/FormField/components/CriteriaTable/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/CriteriaTable/RenderTable/index.js b/src/components/FormField/components/CriteriaTable/RenderTable/index.js index c15d7c8..e888433 100644 --- a/src/components/FormField/components/CriteriaTable/RenderTable/index.js +++ b/src/components/FormField/components/CriteriaTable/RenderTable/index.js @@ -82,17 +82,17 @@ const RenderTable = ({ tableValue = {}, columnsCfg, lastRowCfg, setTableValueFn, ); })} + {!isEmpty(lastRowCfg) - ? + ? {columnsCfg.map((o) => )} - + : null} -
) } diff --git a/src/components/FormField/components/CriteriaTable/index.js b/src/components/FormField/components/CriteriaTable/index.js index 3801617..f965321 100644 --- a/src/components/FormField/components/CriteriaTable/index.js +++ b/src/components/FormField/components/CriteriaTable/index.js @@ -58,10 +58,10 @@ const Table = ({ } } - if (o.predefined) { - item.cell = (info) => info.getValue(); - } else if (o.enableFormula || o.fieldtype === 'numeric') { + if (o.enableFormula || o.fieldtype === 'numeric') { item.cell = NumericFormulaCell; + } else { + item.cell = (info) => info.getValue(); } return item; @@ -84,7 +84,7 @@ const Table = ({ let rowsData = pathOr([obj], ['rowsData'], tableColumns); rowsData = isEmpty(rowsData) ? [obj] : rowsData; setColumnsCfg(stateFieldData); - setRowsCfg({rows: rowsData, total: 0}); + setRowsCfg({ rows: rowsData, total: 0 }); let lastRowData = stateFieldData.reduce((acc, cur) => { const value = cur.enableFormula ? cur.lastRowFormula : (cur.lastRowText ? cur.lastRowText : ''); 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/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 43cf9ac..c925bcd 100644 --- a/src/components/FormField/components/Table/RenderTable/index.js +++ b/src/components/FormField/components/Table/RenderTable/index.js @@ -68,17 +68,17 @@ const RenderTable = ({ rowsData, columnsCfg, lastRowCfg, setRowsFn, disabled }) ); })} + {!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 c925b08..02d33be 100644 --- a/src/components/FormField/components/Table/index.js +++ b/src/components/FormField/components/Table/index.js @@ -176,7 +176,7 @@ const Table = ({ setRowsFn={updateRows} disabled={disabled}/> : null} {!isEmpty(columns) && !shouldDisableNewRows - ?
+ ?
{__('Aggiungi una riga', 'gepafin')}
: null} 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/pages/BandoFormsEdit/components/BuilderElementSettings/components/ElementSetting/index.js b/src/pages/BandoFormsEdit/components/BuilderElementSettings/components/ElementSetting/index.js index cb50706..ed7fa42 100644 --- a/src/pages/BandoFormsEdit/components/BuilderElementSettings/components/ElementSetting/index.js +++ b/src/pages/BandoFormsEdit/components/BuilderElementSettings/components/ElementSetting/index.js @@ -1,6 +1,9 @@ -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'; @@ -19,6 +22,8 @@ import { mimeTypes } from '../../../../../../configData'; const ElementSetting = ({ setting, changeFn, updateDataFn, bandoStatus }) => { + const [existingVars, setExistingVars] = useState([]); + const settingLabels = { label: __('Label', 'gepafin'), placeholder: __('Segnaposto', 'gepafin'), @@ -72,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 { } } + 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} +
: null}
} diff --git a/src/pages/BandoFormsEdit/components/BuilderElementSettings/components/ElementSettingCriteriaTableColumns/index.js b/src/pages/BandoFormsEdit/components/BuilderElementSettings/components/ElementSettingCriteriaTableColumns/index.js index 0053d76..cd1a251 100644 --- a/src/pages/BandoFormsEdit/components/BuilderElementSettings/components/ElementSettingCriteriaTableColumns/index.js +++ b/src/pages/BandoFormsEdit/components/BuilderElementSettings/components/ElementSettingCriteriaTableColumns/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 ElementSettingCriteriaTableColumns = ({ value, @@ -24,13 +25,15 @@ const ElementSettingCriteriaTableColumns = ({ const removeItem = (index) => { let newData = stateFieldData - .toSpliced(index, 1) + .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 = () => { diff --git a/src/pages/BandoFormsEdit/components/BuilderElementSettings/components/ElementSettingTableColumns/index.js b/src/pages/BandoFormsEdit/components/BuilderElementSettings/components/ElementSettingTableColumns/index.js index 30eb834..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 = () => { diff --git a/src/pages/BandoFormsPreview/index.js b/src/pages/BandoFormsPreview/index.js index 4f2de9e..7da1102 100644 --- a/src/pages/BandoFormsPreview/index.js +++ b/src/pages/BandoFormsPreview/index.js @@ -2,7 +2,7 @@ import React, { useState, useEffect } from 'react'; import { __ } from '@wordpress/i18n'; import { useNavigate, useParams } from 'react-router-dom'; import { klona } from 'klona'; -import { head, isNil , isEmpty } from 'ramda'; +import { head, isNil, isEmpty, pathOr } from 'ramda'; import { useForm } from 'react-hook-form'; import 'quill/dist/quill.core.css'; import { evaluate } from 'mathjs'; @@ -84,23 +84,6 @@ const BandoFormsPreview = () => { if (data.status === 'SUCCESS') { setFormName(data.data.label); 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); } storeSet.main.unsetAsyncRequest(); @@ -112,37 +95,39 @@ const BandoFormsPreview = () => { } useEffect(() => { - if (!isEmpty(fieldsWithVars) && !isEmpty(fieldsWithFormula)) { - const updatedFormValues = klona(formValues); - let context = {}; + let updatedFormValues = klona(formValues); + let context = {}; - // eslint-disable-next-line array-callback-return - Object.keys(updatedFormValues).map(fieldId => { - if (!isNil(fieldsWithFormula[fieldId])) { - const formula = fieldsWithFormula[fieldId]; - context = getTokens(formula) - .filter(v => !['false', 'null', 'true'].includes(v)) - .reduce((acc, cur) => { - acc[cur] = isNil(context[cur]) ? 0 : context[cur]; - return acc; - }, {}); - const mathFormula = renderWithDataVars(formula, context); - try { - updatedFormValues[fieldId] = evaluate(mathFormula); - } catch (e) { - console.log('Error in math formula: "', mathFormula, '"', e.message); - updatedFormValues[fieldId] = 0; - } - } - if (!isNil(fieldsWithVars[fieldId])) { - context[fieldsWithVars[fieldId]] = updatedFormValues[fieldId] - } - }); + // 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 (!isEmpty(updatedFormValues) && !equal(updatedFormValues, formValues)) { - reset(updatedFormValues); + 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]); diff --git a/src/pages/ResetPassword/index.js b/src/pages/ResetPassword/index.js index a43724f..6f3364d 100644 --- a/src/pages/ResetPassword/index.js +++ b/src/pages/ResetPassword/index.js @@ -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 { useForm } from 'react-hook-form'; import { classNames } from 'primereact/utils'; -import { isEmpty, isNil } from 'ramda'; +import { isEmpty } from 'ramda'; import { useNavigate, useSearchParams } from 'react-router-dom'; // tools From 682ba42b8fb07a8b4f3d225403a2119cb082f5de Mon Sep 17 00:00:00 2001 From: Vitalii Kiiko Date: Fri, 24 Jan 2025 16:11:41 +0100 Subject: [PATCH 10/27] - evaluation page stats added; --- src/pages/DomandeInstructorManager/index.js | 72 +++++++++++++++++++- src/pages/DomandePreInstructor/index.js | 74 ++++++++++++++++++++- src/service/dashboard-service.js | 4 ++ 3 files changed, 148 insertions(+), 2 deletions(-) diff --git a/src/pages/DomandeInstructorManager/index.js b/src/pages/DomandeInstructorManager/index.js index b593398..fc68cec 100644 --- a/src/pages/DomandeInstructorManager/index.js +++ b/src/pages/DomandeInstructorManager/index.js @@ -1,6 +1,6 @@ import React, { useEffect, useRef, useState } from 'react'; import { __ } from '@wordpress/i18n'; -import { isEmpty } from 'ramda'; +import { isEmpty, pathOr } from 'ramda'; // store import { storeSet } from '../../store'; @@ -20,6 +20,8 @@ import { classNames } from 'primereact/utils'; import { Dropdown } from 'primereact/dropdown'; import { Dialog } from 'primereact/dialog'; import { Button } from 'primereact/button'; +import DashboardService from '../../service/dashboard-service'; +import NumberFlow from '@number-flow/react'; const DomandeInstructorManager = () => { const [loading, setLoading] = useState(false); @@ -30,6 +32,7 @@ const DomandeInstructorManager = () => { const [chosenApplication, setChosenApplication] = useState(0); const [updaterString, setUpdaterString] = useState(''); const toast = useRef(null); + const [mainStats, setMainStats] = useState({}); const getRolesCallback = (data) => { if (data.status === 'SUCCESS') { @@ -123,6 +126,22 @@ const DomandeInstructorManager = () => { storeSet.main.unsetAsyncRequest(); } + const getStats = (data) => { + if (data.status === 'SUCCESS') { + setMainStats(data.data); + } + } + + const errGetStats = () => {} + + const getStatValue = (key, fallback = '') => { + return pathOr(fallback, [key], mainStats); + } + + useEffect(() => { + DashboardService.getEvaluationsStats(getStats, errGetStats); + }, []); + useEffect(() => { if (roleIds.length > 0) { setLoading(true); @@ -151,6 +170,57 @@ const DomandeInstructorManager = () => {
+
+

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

+
+
+ {__('Totale domande', 'gepafin')} + +
+
+ {__('In soccorso', 'gepafin')} + +
+
+ {__('In valutazione', 'gepafin')} + +
+
+ {__('Completate', 'gepafin')} + +
+
+ {__('Tempo medio di valutazione', 'gepafin')} + +
+
+ {__('Domande in scadenza (48h)', 'gepafin')} + +
+
+
+ +
+

{__('Domande pubblicate', 'gepafin')}

diff --git a/src/pages/DomandePreInstructor/index.js b/src/pages/DomandePreInstructor/index.js index 1a24b3d..00abd73 100644 --- a/src/pages/DomandePreInstructor/index.js +++ b/src/pages/DomandePreInstructor/index.js @@ -1,10 +1,31 @@ -import React from 'react'; +import React, { useEffect, useState } from 'react'; import { __ } from '@wordpress/i18n'; // components import PreInstructorDomandeTable from '../DashboardPreInstructor/components/PreInstructorDomandeTable'; +import { pathOr } from 'ramda'; +import DashboardService from '../../service/dashboard-service'; +import NumberFlow from '@number-flow/react'; const DomandePreInstructor = () => { + const [mainStats, setMainStats] = useState({}); + + const getStats = (data) => { + if (data.status === 'SUCCESS') { + setMainStats(data.data); + } + } + + const errGetStats = () => {} + + const getStatValue = (key, fallback = '') => { + return pathOr(fallback, [key], mainStats); + } + + useEffect(() => { + DashboardService.getEvaluationsStats(getStats, errGetStats); + }, []); + return(
@@ -16,6 +37,57 @@ const DomandePreInstructor = () => {
+ +
+ +
+

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

+
+
+ {__('Totale domande', 'gepafin')} + +
+
+ {__('In soccorso', 'gepafin')} + +
+
+ {__('In valutazione', 'gepafin')} + +
+
+ {__('Completate', 'gepafin')} + +
+
+ {__('Tempo medio di valutazione', 'gepafin')} + +
+
+ {__('Domande in scadenza (48h)', 'gepafin')} + +
+
+
) } diff --git a/src/service/dashboard-service.js b/src/service/dashboard-service.js index 6951e9c..093db59 100644 --- a/src/service/dashboard-service.js +++ b/src/service/dashboard-service.js @@ -16,6 +16,10 @@ export default class DashboardService { NetworkService.get(`${API_BASE_URL}/dashboard/amendment`, callback, errCallback); }; + static getEvaluationsStats = (callback, errCallback) => { + NetworkService.get(`${API_BASE_URL}/dashboard/evaluation`, callback, errCallback); + }; + static getBeneficiaryStatsForCompany = (id, callback, errCallback) => { NetworkService.get(`${API_BASE_URL}/dashboard/beneficiary/company/${id}`, callback, errCallback); }; From f23ec7a7a0c5f51096c30eeb109b117fae164fc2 Mon Sep 17 00:00:00 2001 From: Vitalii Kiiko Date: Fri, 24 Jan 2025 16:58:35 +0100 Subject: [PATCH 11/27] - added fields calculation logic to application page; --- package.json | 36 ++++++++-------- src/pages/BandoApplication/index.js | 61 +++++++++++++++------------- src/pages/BandoFormsPreview/index.js | 2 - 3 files changed, 51 insertions(+), 48 deletions(-) diff --git a/package.json b/package.json index 90df6a6..7edb643 100644 --- a/package.json +++ b/package.json @@ -4,24 +4,24 @@ "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", @@ -30,15 +30,15 @@ "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", @@ -47,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/pages/BandoApplication/index.js b/src/pages/BandoApplication/index.js index 4067d02..965a2dd 100644 --- a/src/pages/BandoApplication/index.js +++ b/src/pages/BandoApplication/index.js @@ -543,37 +543,39 @@ const BandoApplication = () => { : ['.p7m']; useEffect(() => { - if (!isEmpty(fieldsWithVars) && !isEmpty(fieldsWithFormula)) { - const updatedFormValues = klona(formValues); - let context = {}; + let updatedFormValues = klona(formValues); + let context = {}; - // eslint-disable-next-line array-callback-return - Object.keys(updatedFormValues).map(fieldId => { - if (!isNil(fieldsWithFormula[fieldId])) { - const formula = fieldsWithFormula[fieldId]; - context = getTokens(formula) - .filter(v => !['false', 'null', 'true'].includes(v)) - .reduce((acc, cur) => { - acc[cur] = isNil(context[cur]) ? 0 : context[cur]; - return acc; - }, {}); - const mathFormula = renderWithDataVars(formula, context); - try { - updatedFormValues[fieldId] = evaluate(mathFormula); - } catch (e) { - console.log('Error in math formula: "', mathFormula, '"', e.message); - updatedFormValues[fieldId] = 0; - } - } - if (!isNil(fieldsWithVars[fieldId])) { - context[fieldsWithVars[fieldId]] = updatedFormValues[fieldId] - } - }); + // 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 (!isEmpty(updatedFormValues) && !equal(updatedFormValues, formValues)) { - reset(updatedFormValues); + 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]); @@ -663,7 +665,10 @@ const BandoApplication = () => { const text = head(o.settings.filter(o => o.name === 'text')); const placeholder = head(o.settings.filter(o => o.name === 'placeholder')); const options = head(o.settings.filter(o => o.name === 'options')); - const tableColumns = head(o.settings.filter(o => o.name === 'table_columns')); + 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')); diff --git a/src/pages/BandoFormsPreview/index.js b/src/pages/BandoFormsPreview/index.js index 7da1102..332700f 100644 --- a/src/pages/BandoFormsPreview/index.js +++ b/src/pages/BandoFormsPreview/index.js @@ -40,8 +40,6 @@ const BandoFormsPreview = () => { const navigate = useNavigate(); const [formData, setFormData] = useState([]); const [formName, setFormName] = useState(''); - const [fieldsWithVars, setFieldsWithVars] = useState({}); - const [fieldsWithFormula, setFieldsWithFormula] = useState({}); const isAsyncRequest = useStore().main.isAsyncRequest(); const { control, From 2a501aa89d8c5ff0e17181ceb8ee01abf9a04c91 Mon Sep 17 00:00:00 2001 From: Vitalii Kiiko Date: Mon, 27 Jan 2025 12:51:27 +0100 Subject: [PATCH 12/27] - fixed preview of the application; --- .../CriteriaTable/RenderTable/index.js | 3 +- .../components/LastRowCell/index.js | 3 +- src/pages/BandoApplicationPreview/index.js | 55 ++++++++++++++++++- 3 files changed, 56 insertions(+), 5 deletions(-) diff --git a/src/components/FormField/components/CriteriaTable/RenderTable/index.js b/src/components/FormField/components/CriteriaTable/RenderTable/index.js index e888433..822df3a 100644 --- a/src/components/FormField/components/CriteriaTable/RenderTable/index.js +++ b/src/components/FormField/components/CriteriaTable/RenderTable/index.js @@ -6,6 +6,7 @@ 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) @@ -31,7 +32,7 @@ const RenderTable = ({ tableValue = {}, columnsCfg, lastRowCfg, setTableValueFn, .map(v => isEmpty(v) || isNil(v) ? 0 : v); if (cellValue === 'sum') { - total = sum(getAllRowsValues); + total = getNumberFormatted(sum(getAllRowsValues)); } else { total = 0; } 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 84bd57b..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,4 +1,5 @@ import { head, isEmpty, isNil, sum } from 'ramda'; +import getNumberFormatted from '../../../../../../../helpers/getNumberFormatted'; const LastRowCell = ({columnId, lastRowCfg, columnMeta = {}, getColumnDataFn}) => { const cellData = head(lastRowCfg.filter(o => !isNil(o[columnId]))); @@ -9,7 +10,7 @@ const LastRowCell = ({columnId, lastRowCfg, 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/pages/BandoApplicationPreview/index.js b/src/pages/BandoApplicationPreview/index.js index 15a7de3..89b75a5 100644 --- a/src/pages/BandoApplicationPreview/index.js +++ b/src/pages/BandoApplicationPreview/index.js @@ -1,7 +1,7 @@ import React, { useState, useEffect, useRef, useMemo } from 'react'; import { __, sprintf } from '@wordpress/i18n'; import { useParams } from 'react-router-dom'; -import { head, isEmpty, pathOr } from 'ramda'; +import { head, isEmpty, isNil, pathOr } from 'ramda'; import { useForm } from 'react-hook-form'; import 'quill/dist/quill.core.css'; @@ -33,6 +33,11 @@ import { Toast } from 'primereact/toast'; import { Messages } from 'primereact/messages'; import ApplicationSteps from '../BandoApplication/ApplicationSteps'; import BlockingOverlay from '../../components/BlockingOverlay'; +import { klona } from 'klona'; +import getTokens from '../../helpers/getTokens'; +import renderWithDataVars from '../../helpers/renderWithDataVars'; +import { evaluate } from 'mathjs'; +import equal from 'fast-deep-equal'; const BandoApplicationPreview = () => { 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 = () => {
: Date: Tue, 28 Jan 2025 14:20:28 +0100 Subject: [PATCH 13/27] - saving progress; --- src/assets/scss/components/layout.scss | 14 + .../components/AppSidebar/index.js | 85 ++- .../components/AllBandiTable/index.js | 149 +++++ src/pages/BandiPreInstructor/index.js | 27 + src/pages/BandoViewPreInstructor/index.js | 274 ++++++++ .../InstructorManagerMieDomandeTable/index.js | 195 ++++++ src/pages/DashboardInstructorManager/index.js | 119 ++++ .../PreInstructorDomandeTable/index.js | 41 +- src/pages/DashboardPreInstructor/index.js | 84 ++- .../DomandaEditInstructorManager/index.js | 14 +- src/pages/DomandaEditPreInstructor/index.js | 4 + src/pages/DomandeArchive/index.js | 12 +- .../DomandeArchivePreInstructor/index.js | 29 + src/pages/DomandeInstructorManager/index.js | 21 +- .../DomandeMieInstructorManager/index.js | 37 ++ src/pages/DomandePreInstructor/index.js | 80 +-- .../SoccorsoAddInstructorManager/index.js | 351 +++++++++++ .../SoccorsoEditInstructorManager/index.js | 587 ++++++++++++++++++ .../SoccorsoIstruttorioPreInstructor/index.js | 12 +- src/routes.js | 53 +- 20 files changed, 2021 insertions(+), 167 deletions(-) create mode 100644 src/pages/BandiPreInstructor/components/AllBandiTable/index.js create mode 100644 src/pages/BandiPreInstructor/index.js create mode 100644 src/pages/BandoViewPreInstructor/index.js create mode 100644 src/pages/DashboardInstructorManager/components/InstructorManagerMieDomandeTable/index.js create mode 100644 src/pages/DashboardInstructorManager/index.js create mode 100644 src/pages/DomandeArchivePreInstructor/index.js create mode 100644 src/pages/DomandeMieInstructorManager/index.js create mode 100644 src/pages/SoccorsoAddInstructorManager/index.js create mode 100644 src/pages/SoccorsoEditInstructorManager/index.js 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/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 +
    +
    + +
    +

    {__('Contatti per Assistenza', 'gepafin')}

    +
    +

    Email: {data.email}

    + {!isNil(data.phoneNumber) ? +

    {__('Telefono', 'gepafin')}: +39 {data.phoneNumber}

    : null} +
    +
    +
    + : <> + + + + + + + + + } +
    + ) + +} + +export default BandoViewPreInstructor; \ No newline at end of file diff --git a/src/pages/DashboardInstructorManager/components/InstructorManagerMieDomandeTable/index.js b/src/pages/DashboardInstructorManager/components/InstructorManagerMieDomandeTable/index.js new file mode 100644 index 0000000..537aefd --- /dev/null +++ b/src/pages/DashboardInstructorManager/components/InstructorManagerMieDomandeTable/index.js @@ -0,0 +1,195 @@ +import React, { useState, useEffect} from 'react'; +import { __ } from '@wordpress/i18n'; +import { is, uniq, isNil } from 'ramda'; +import { Link } from 'react-router-dom'; + +// api +import AssignedApplicationService from '../../../../service/assigned-application-service'; + +// tools +import getBandoLabel from '../../../../helpers/getBandoLabel'; +import getBandoSeverity from '../../../../helpers/getBandoSeverity'; + +// components +import { FilterMatchMode, FilterOperator } from 'primereact/api'; +import { DataTable } from 'primereact/datatable'; +import { Column } from 'primereact/column'; +import { Button } from 'primereact/button'; +import { Calendar } from 'primereact/calendar'; +import ProperBandoLabel from '../../../../components/ProperBandoLabel'; +import { Dropdown } from 'primereact/dropdown'; +import { Tag } from 'primereact/tag'; + +import translationStrings from '../../../../translationStringsForComponents'; +import { useStore } from '../../../../store'; + +const APP_HUB_ID = process.env.REACT_APP_HUB_ID; + +const InstructorManagerMieDomandeTable = ({ userId = null, statuses = [] }) => { + const [items, setItems] = useState(null); + const [filters, setFilters] = useState(null); + const [localAsyncRequest, setLocalAsyncRequest] = useState(false); + const [statusesForFilter, setStatusesForFilter] = useState([]); + const userData = useStore().main.userData(); + + useEffect(() => { + if (!isNil(userId)) { + setLocalAsyncRequest(true); + + if (userId === 0) { + AssignedApplicationService.getAssignedApplications(getCallback, errGetCallbacks, [ + ['statuses', statuses] + ]); + } else { + AssignedApplicationService.getAssignedApplications(getCallback, errGetCallbacks, [ + ['userId', userId], + ['statuses', statuses] + ]); + } + } + }, [userId]); + + const getCallback = (data) => { + if (data.status === 'SUCCESS') { + setItems(getFormattedData(data.data)); + setStatusesForFilter(uniq(data.data.map(o => o.status))) + initFilters(); + } + setLocalAsyncRequest(false); + } + + const errGetCallbacks = (data) => { + setLocalAsyncRequest(false); + } + + const getFormattedData = (data) => { + return data.map((d) => { + d.evaluationEndDate = is(String, d.evaluationEndDate) ? new Date(d.evaluationEndDate) : (d.evaluationEndDate ? d.evaluationEndDate : ''); + d.submissionDate = is(String, d.submissionDate) ? new Date(d.submissionDate) : (d.submissionDate ? d.submissionDate : ''); + return d; + }); + }; + + const formatDate = (value) => { + return value.toLocaleDateString('it-IT', { + day: '2-digit', + month: '2-digit', + year: 'numeric' + }); + }; + + const clearFilter = () => { + initFilters(); + }; + + const initFilters = () => { + setFilters({ + global: { value: null, matchMode: FilterMatchMode.CONTAINS }, + callName: { + operator: FilterOperator.AND, + constraints: [{ value: null, matchMode: FilterMatchMode.STARTS_WITH }] + }, + companyName: { + operator: FilterOperator.AND, + constraints: [{ value: null, matchMode: FilterMatchMode.STARTS_WITH }] + }, + submissionDate: { + operator: FilterOperator.AND, + constraints: [{ value: null, matchMode: FilterMatchMode.DATE_IS }] + }, + evaluationEndDate: { + operator: FilterOperator.AND, + constraints: [{ value: null, matchMode: FilterMatchMode.DATE_IS }] + } + }); + }; + + const renderHeader = () => { + return ( +
    +
    + ); + }; + + const dateAppliedBodyTemplate = (rowData) => { + return formatDate(rowData.submissionDate); + }; + + const dateEndBodyTemplate = (rowData) => { + return formatDate(rowData.evaluationEndDate); + }; + + 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) => { + const label = ['OPEN', 'SOCCORSO'].includes(rowData.status) && userData.id === rowData.userId + ? __('Valuta', 'gepafin') + : __('Mostra', 'gepafin'); + return +
    +
    +
    + ) +} + +export default DashboardInstructorManager; \ No newline at end of file diff --git a/src/pages/DashboardPreInstructor/components/PreInstructorDomandeTable/index.js b/src/pages/DashboardPreInstructor/components/PreInstructorDomandeTable/index.js index d8c5188..43b2cf1 100644 --- a/src/pages/DashboardPreInstructor/components/PreInstructorDomandeTable/index.js +++ b/src/pages/DashboardPreInstructor/components/PreInstructorDomandeTable/index.js @@ -1,11 +1,8 @@ import React, { useState, useEffect} from 'react'; import { __ } from '@wordpress/i18n'; -import { is, uniq } from 'ramda'; +import { is, uniq, isNil } from 'ramda'; import { Link } from 'react-router-dom'; -// store -import { useStore } from '../../../../store'; - // api import AssignedApplicationService from '../../../../service/assigned-application-service'; @@ -24,27 +21,38 @@ import { Dropdown } from 'primereact/dropdown'; import { Tag } from 'primereact/tag'; import translationStrings from '../../../../translationStringsForComponents'; +import { useStore } from '../../../../store'; const APP_HUB_ID = process.env.REACT_APP_HUB_ID; -const PreInstructorDomandeTable = () => { - const userData = useStore().main.userData(); +const PreInstructorDomandeTable = ({ userId = null, statuses = [] }) => { const [items, setItems] = useState(null); const [filters, setFilters] = useState(null); const [localAsyncRequest, setLocalAsyncRequest] = useState(false); - const [statuses, setStatuses] = useState([]); + const [statusesForFilter, setStatusesForFilter] = useState([]); + const userData = useStore().main.userData(); useEffect(() => { - setLocalAsyncRequest(true); - AssignedApplicationService.getAssignedApplications(getCallback, errGetCallbacks, [ - ['userId', userData.id] - ]); - }, []); + if (!isNil(userId)) { + setLocalAsyncRequest(true); + + if (userId === 0) { + AssignedApplicationService.getAssignedApplications(getCallback, errGetCallbacks, [ + ['statuses', statuses] + ]); + } else { + AssignedApplicationService.getAssignedApplications(getCallback, errGetCallbacks, [ + ['userId', userId], + ['statuses', statuses] + ]); + } + } + }, [userId]); const getCallback = (data) => { if (data.status === 'SUCCESS') { setItems(getFormattedData(data.data)); - setStatuses(uniq(data.data.map(o => o.status))) + setStatusesForFilter(uniq(data.data.map(o => o.status))) initFilters(); } setLocalAsyncRequest(false); @@ -121,7 +129,7 @@ const PreInstructorDomandeTable = () => { }; const statusFilterTemplate = (options) => { - return options.filterCallback(e.value, options.index)} itemTemplate={statusItemTemplate} placeholder={translationStrings.selectOneLabel} className="p-column-filter" showClear />; + return options.filterCallback(e.value, options.index)} itemTemplate={statusItemTemplate} placeholder={translationStrings.selectOneLabel} className="p-column-filter" showClear />; }; const statusItemTemplate = (option) => { @@ -129,8 +137,11 @@ const PreInstructorDomandeTable = () => { }; const actionsBodyTemplate = (rowData) => { + const label = ['OPEN', 'SOCCORSO'].includes(rowData.status) && userData.id === rowData.userId + ? __('Valuta', 'gepafin') + : __('Mostra', 'gepafin'); return - + + + + + + + + + + + + ); + }; + + 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} From 7cf1b044be69cdb870a898c705d680d84650f4d6 Mon Sep 17 00:00:00 2001 From: Vitalii Kiiko Date: Tue, 28 Jan 2025 14:42:21 +0100 Subject: [PATCH 14/27] - fix for table value numeric with comma; --- src/pages/BandiPreInstructor/index.js | 4 ---- src/pages/BandoApplication/index.js | 16 +--------------- src/pages/BandoApplicationPreview/index.js | 2 +- src/pages/BandoFormsPreview/index.js | 2 +- src/pages/BandoViewPreInstructor/index.js | 4 ++-- src/pages/DomandeInstructorManager/index.js | 5 ----- 6 files changed, 5 insertions(+), 28 deletions(-) diff --git a/src/pages/BandiPreInstructor/index.js b/src/pages/BandiPreInstructor/index.js index 8388166..a3f3f9c 100644 --- a/src/pages/BandiPreInstructor/index.js +++ b/src/pages/BandiPreInstructor/index.js @@ -1,14 +1,10 @@ import React from 'react'; import { __ } from '@wordpress/i18n'; -import { useNavigate } from 'react-router-dom'; // components import AllBandiTable from './components/AllBandiTable'; -import { Button } from 'primereact/button'; const BandiPreInstructor = () => { - const navigate = useNavigate(); - return(
    diff --git a/src/pages/BandoApplication/index.js b/src/pages/BandoApplication/index.js index 965a2dd..d76198b 100644 --- a/src/pages/BandoApplication/index.js +++ b/src/pages/BandoApplication/index.js @@ -53,8 +53,6 @@ const BandoApplication = () => { const [isExpired, setIsExpired] = useState(false); const [formData, setFormData] = useState([]); const [formInitialData, setFormInitialData] = useState(null); - const [fieldsWithVars, setFieldsWithVars] = useState({}); - const [fieldsWithFormula, setFieldsWithFormula] = useState({}); const [bandoTitle, setBandoTitle] = useState(''); const [bandoId, setBandoId] = useState(0); const [formId, setFormId] = useState(''); @@ -364,8 +362,6 @@ const BandoApplication = () => { dynamicData = wrap(dynamicData).set(['custom', 'userFullName'], userFullName).value(); const legalRepresentantName = company.isLegalRepresentant ? userFullName : ''; dynamicData = wrap(dynamicData).set(['custom', 'legalRepresentant'], legalRepresentantName).value(); - let allvars = {}; - let allformulas = {}; if (data.data.applicationFormResponse.content) { // eslint-disable-next-line array-callback-return @@ -373,14 +369,6 @@ const BandoApplication = () => { if (o.dynamicData && !isEmpty(o.dynamicData)) { formDataInitial[o.id] = pathOr('', o.dynamicData.split('.'), dynamicData); } - 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; - } }); } @@ -398,8 +386,6 @@ const BandoApplication = () => { } reset(); - setFieldsWithVars(allvars); - setFieldsWithFormula(allformulas); setFormInitialData(formDataInitial); } storeSet.main.unsetAsyncRequest(); @@ -555,7 +541,7 @@ const BandoApplication = () => { context = getTokens(formula.value) .filter(v => !['false', 'null', 'true'].includes(v)) .reduce((acc, cur) => { - acc[cur] = isNil(context[cur]) ? 0 : context[cur]; + acc[cur] = isNil(context[cur]) ? 0 : parseFloat(context[cur]); return acc; }, context); const mathFormula = renderWithDataVars(formula.value, context); diff --git a/src/pages/BandoApplicationPreview/index.js b/src/pages/BandoApplicationPreview/index.js index 89b75a5..82cfe58 100644 --- a/src/pages/BandoApplicationPreview/index.js +++ b/src/pages/BandoApplicationPreview/index.js @@ -272,7 +272,7 @@ const BandoApplicationPreview = () => { context = getTokens(formula.value) .filter(v => !['false', 'null', 'true'].includes(v)) .reduce((acc, cur) => { - acc[cur] = isNil(context[cur]) ? 0 : context[cur]; + acc[cur] = isNil(context[cur]) ? 0 : parseFloat(context[cur]); return acc; }, context); const mathFormula = renderWithDataVars(formula.value, context); diff --git a/src/pages/BandoFormsPreview/index.js b/src/pages/BandoFormsPreview/index.js index 332700f..c48d252 100644 --- a/src/pages/BandoFormsPreview/index.js +++ b/src/pages/BandoFormsPreview/index.js @@ -105,7 +105,7 @@ const BandoFormsPreview = () => { context = getTokens(formula.value) .filter(v => !['false', 'null', 'true'].includes(v)) .reduce((acc, cur) => { - acc[cur] = isNil(context[cur]) ? 0 : context[cur]; + acc[cur] = isNil(context[cur]) ? 0 : parseFloat(context[cur]); return acc; }, context); const mathFormula = renderWithDataVars(formula.value, context); diff --git a/src/pages/BandoViewPreInstructor/index.js b/src/pages/BandoViewPreInstructor/index.js index 52bbe25..40a2349 100644 --- a/src/pages/BandoViewPreInstructor/index.js +++ b/src/pages/BandoViewPreInstructor/index.js @@ -32,9 +32,9 @@ const BandoViewPreInstructor = () => { const [newQuestion, setNewQuestion] = useState(''); const bandoMsgs = useRef(null); - const closePreview = () => { + /*const closePreview = () => { navigate(`/bandi/${id}`); - } + }*/ const getCallback = (data) => { if (data.status === 'SUCCESS') { diff --git a/src/pages/DomandeInstructorManager/index.js b/src/pages/DomandeInstructorManager/index.js index fbe1232..35957b5 100644 --- a/src/pages/DomandeInstructorManager/index.js +++ b/src/pages/DomandeInstructorManager/index.js @@ -21,7 +21,6 @@ import { Dropdown } from 'primereact/dropdown'; import { Dialog } from 'primereact/dialog'; import { Button } from 'primereact/button'; import DashboardService from '../../service/dashboard-service'; -import NumberFlow from '@number-flow/react'; const DomandeInstructorManager = () => { const [loading, setLoading] = useState(false); @@ -134,10 +133,6 @@ const DomandeInstructorManager = () => { const errGetStats = () => {} - const getStatValue = (key, fallback = '') => { - return pathOr(fallback, [key], mainStats); - } - useEffect(() => { DashboardService.getEvaluationsStats(getStats, errGetStats); }, []); From 2187e4a36d5ae7f63d51a808f26374f02a0b516f Mon Sep 17 00:00:00 2001 From: Vitalii Kiiko Date: Tue, 28 Jan 2025 15:33:21 +0100 Subject: [PATCH 15/27] - fixed issue with new table skips decimals; --- src/helpers/parseCommaDecimal.js | 3 +++ src/pages/BandoApplication/index.js | 4 +++- src/pages/BandoApplicationPreview/index.js | 3 ++- src/pages/BandoFormsPreview/index.js | 3 ++- src/pages/BandoViewPreInstructor/index.js | 7 +------ 5 files changed, 11 insertions(+), 9 deletions(-) create mode 100644 src/helpers/parseCommaDecimal.js diff --git a/src/helpers/parseCommaDecimal.js b/src/helpers/parseCommaDecimal.js new file mode 100644 index 0000000..fe1c8d8 --- /dev/null +++ b/src/helpers/parseCommaDecimal.js @@ -0,0 +1,3 @@ +const parseCommaDecimal = (value = '') => parseFloat(String(value).replace(',', '.')); + +export default parseCommaDecimal; \ No newline at end of file diff --git a/src/pages/BandoApplication/index.js b/src/pages/BandoApplication/index.js index d76198b..4ed2551 100644 --- a/src/pages/BandoApplication/index.js +++ b/src/pages/BandoApplication/index.js @@ -46,6 +46,7 @@ import { Dialog } from 'primereact/dialog'; import FileuploadApplicationSignedPdf from '../../components/FileuploadApplicationSignedPdf'; import { defaultMaxFileSize } from '../../configData'; +import parseCommaDecimal from '../../helpers/parseCommaDecimal'; const BandoApplication = () => { const chosenCompanyId = useStore().main.chosenCompanyId(); @@ -541,9 +542,10 @@ const BandoApplication = () => { context = getTokens(formula.value) .filter(v => !['false', 'null', 'true'].includes(v)) .reduce((acc, cur) => { - acc[cur] = isNil(context[cur]) ? 0 : parseFloat(context[cur]); + acc[cur] = isNil(context[cur]) ? 0 : parseCommaDecimal(context[cur]); return acc; }, context); + const mathFormula = renderWithDataVars(formula.value, context); try { updatedFormValues[o.id] = evaluate(mathFormula); diff --git a/src/pages/BandoApplicationPreview/index.js b/src/pages/BandoApplicationPreview/index.js index 82cfe58..a21913e 100644 --- a/src/pages/BandoApplicationPreview/index.js +++ b/src/pages/BandoApplicationPreview/index.js @@ -38,6 +38,7 @@ import getTokens from '../../helpers/getTokens'; import renderWithDataVars from '../../helpers/renderWithDataVars'; import { evaluate } from 'mathjs'; import equal from 'fast-deep-equal'; +import parseCommaDecimal from '../../helpers/parseCommaDecimal'; const BandoApplicationPreview = () => { const { id } = useParams(); @@ -272,7 +273,7 @@ const BandoApplicationPreview = () => { context = getTokens(formula.value) .filter(v => !['false', 'null', 'true'].includes(v)) .reduce((acc, cur) => { - acc[cur] = isNil(context[cur]) ? 0 : parseFloat(context[cur]); + acc[cur] = isNil(context[cur]) ? 0 : parseCommaDecimal(context[cur]); return acc; }, context); const mathFormula = renderWithDataVars(formula.value, context); diff --git a/src/pages/BandoFormsPreview/index.js b/src/pages/BandoFormsPreview/index.js index c48d252..8a1402c 100644 --- a/src/pages/BandoFormsPreview/index.js +++ b/src/pages/BandoFormsPreview/index.js @@ -34,6 +34,7 @@ import { import renderHtmlContent from '../../helpers/renderHtmlContent'; import renderWithDataVars from '../../helpers/renderWithDataVars'; import getTokens from '../../helpers/getTokens'; +import parseCommaDecimal from '../../helpers/parseCommaDecimal'; const BandoFormsPreview = () => { const { id, formId } = useParams(); @@ -105,7 +106,7 @@ const BandoFormsPreview = () => { context = getTokens(formula.value) .filter(v => !['false', 'null', 'true'].includes(v)) .reduce((acc, cur) => { - acc[cur] = isNil(context[cur]) ? 0 : parseFloat(context[cur]); + acc[cur] = isNil(context[cur]) ? 0 : parseCommaDecimal(context[cur]); return acc; }, context); const mathFormula = renderWithDataVars(formula.value, context); diff --git a/src/pages/BandoViewPreInstructor/index.js b/src/pages/BandoViewPreInstructor/index.js index 40a2349..76f06cb 100644 --- a/src/pages/BandoViewPreInstructor/index.js +++ b/src/pages/BandoViewPreInstructor/index.js @@ -1,6 +1,6 @@ import React, { useState, useEffect, useRef } from 'react'; import { __ } from '@wordpress/i18n'; -import { useNavigate, useParams } from 'react-router-dom'; +import { useParams } from 'react-router-dom'; import { is, isEmpty, isNil } from 'ramda'; import 'quill/dist/quill.core.css'; @@ -27,15 +27,10 @@ const REACT_APP_HUB_ID = process.env.REACT_APP_HUB_ID; const BandoViewPreInstructor = () => { const isAsyncRequest = useStore().main.isAsyncRequest(); const { id } = useParams(); - const navigate = useNavigate(); const [data, setData] = useState({}); const [newQuestion, setNewQuestion] = useState(''); const bandoMsgs = useRef(null); - /*const closePreview = () => { - navigate(`/bandi/${id}`); - }*/ - const getCallback = (data) => { if (data.status === 'SUCCESS') { setData(getFormattedBandiData(data.data)); From 457194812ba292c34d7d5bebfb3523d584e9c690 Mon Sep 17 00:00:00 2001 From: Vitalii Kiiko Date: Wed, 29 Jan 2025 11:00:55 +0100 Subject: [PATCH 16/27] - added updating status for initiating evaluation process; - fixed issues with displaying and calcuating decimals in appl form; --- src/assets/scss/components/misc.scss | 6 +- .../components/BuilderElement/index.js | 16 ++ .../InstructorManagerMieDomandeTable/index.js | 49 ++++- .../PreInstructorDomandeTable/index.js | 49 ++++- .../DomandaEditInstructorManager/index.js | 18 +- src/pages/DomandaEditPreInstructor/index.js | 16 +- src/pages/DomandeBeneficiario/index.js | 4 +- .../index.js | 95 ++++++++++ .../InstructorManagerSoccorsiTable/index.js | 175 ++++++++++++++++++ .../index.js | 23 +++ .../PreInstructorSoccorsiTable/index.js | 23 ++- .../SoccorsoIstruttorioPreInstructor/index.js | 12 +- src/routes.js | 6 +- src/service/amendments-service.js | 4 +- src/service/assigned-application-service.js | 4 + 15 files changed, 449 insertions(+), 51 deletions(-) create mode 100644 src/pages/SoccorsoIstruttorioInstructorManager/index.js create mode 100644 src/pages/SoccorsoIstruttorioMioInstructorManager/components/InstructorManagerSoccorsiTable/index.js create mode 100644 src/pages/SoccorsoIstruttorioMioInstructorManager/index.js diff --git a/src/assets/scss/components/misc.scss b/src/assets/scss/components/misc.scss index 309e5ff..030a350 100644 --- a/src/assets/scss/components/misc.scss +++ b/src/assets/scss/components/misc.scss @@ -34,7 +34,11 @@ &.p-tag-secondary { background-color: var(--table-border-color); } - + + &.p-tag-tertiary { + background-color: var(--card-full-background-color-6); + } + .p-tag-value { color: var(--menuitem-active-color); } diff --git a/src/pages/BandoFormsEdit/components/BuilderElement/index.js b/src/pages/BandoFormsEdit/components/BuilderElement/index.js index 71c0324..83d22af 100644 --- a/src/pages/BandoFormsEdit/components/BuilderElement/index.js +++ b/src/pages/BandoFormsEdit/components/BuilderElement/index.js @@ -19,6 +19,8 @@ const BuilderElement = ({ id, name, label, index, bandoStatus }) => { const element = head(elements.filter(o => o.id === id)); const [isVariable, setIsVariable] = useState('secondary'); const [isFormula, setIsFormula] = useState('secondary'); + const [isRequestedAmount, setIsRequestedAmount] = useState(false); + const [isDelegation, setIsDelegation] = useState(false); const [{ handlerId }, drop] = useDrop({ accept: ItemTypes.FIELD, @@ -105,6 +107,8 @@ const BuilderElement = ({ id, name, label, index, bandoStatus }) => { useEffect(() => { const variable = head(element.settings.filter(o => o.name === 'variable')); const formula = head(element.settings.filter(o => o.name === 'formula')); + const isRequestedAmount = head(element.settings.filter(o => o.name === 'isRequestedAmount')); + const isDelegation = head(element.settings.filter(o => o.name === 'isDelegation')); if (variable && !isEmpty(variable.value)) { setIsVariable('warning'); @@ -113,6 +117,14 @@ const BuilderElement = ({ id, name, label, index, bandoStatus }) => { if (formula && !isEmpty(formula.value)) { setIsFormula('warning'); } + + if (isRequestedAmount && !isEmpty(isRequestedAmount.value) && isRequestedAmount.value) { + setIsRequestedAmount('tertiary'); + } + + if (isDelegation && !isEmpty(isDelegation.value) && isDelegation.value) { + setIsDelegation('tertiary'); + } }, [elements]); return ( @@ -128,6 +140,10 @@ const BuilderElement = ({ id, name, label, index, bandoStatus }) => { ? : null} {name === 'numberinput' ? : null} + {isRequestedAmount + ? : null} + {isDelegation + ? : null}
    diff --git a/src/pages/DashboardInstructorManager/components/InstructorManagerMieDomandeTable/index.js b/src/pages/DashboardInstructorManager/components/InstructorManagerMieDomandeTable/index.js index 537aefd..752347f 100644 --- a/src/pages/DashboardInstructorManager/components/InstructorManagerMieDomandeTable/index.js +++ b/src/pages/DashboardInstructorManager/components/InstructorManagerMieDomandeTable/index.js @@ -1,7 +1,7 @@ import React, { useState, useEffect} from 'react'; import { __ } from '@wordpress/i18n'; -import { is, uniq, isNil } from 'ramda'; -import { Link } from 'react-router-dom'; +import { is, uniq, isNil, head } from 'ramda'; +import { Link, useNavigate } from 'react-router-dom'; // api import AssignedApplicationService from '../../../../service/assigned-application-service'; @@ -22,10 +22,12 @@ import { Tag } from 'primereact/tag'; import translationStrings from '../../../../translationStringsForComponents'; import { useStore } from '../../../../store'; +import { klona } from 'klona'; const APP_HUB_ID = process.env.REACT_APP_HUB_ID; const InstructorManagerMieDomandeTable = ({ userId = null, statuses = [] }) => { + const navigate = useNavigate(); const [items, setItems] = useState(null); const [filters, setFilters] = useState(null); const [localAsyncRequest, setLocalAsyncRequest] = useState(false); @@ -136,13 +138,44 @@ const InstructorManagerMieDomandeTable = ({ userId = null, statuses = [] }) => { return ; }; + const handleInitiateEvaluation = (id) => { + setLocalAsyncRequest(true); + AssignedApplicationService.updateStatusAssignedApplication(id, getInitEvalCallback, errInitEvalCallbacks, [ + ['status', 'OPEN'] + ]) + } + + const getInitEvalCallback = (resp) => { + if (resp.status === 'SUCCESS') { + const evaluation = klona(head(getFormattedData([resp.data]))); + const newItems = items.map(o => o.id === evaluation.id ? evaluation : o); + setItems(newItems); + navigate(`/mie-domande/${evaluation.applicationId}`); + } + setLocalAsyncRequest(false); + } + + const errInitEvalCallbacks = (resp) => { + setLocalAsyncRequest(false); + } + const actionsBodyTemplate = (rowData) => { - const label = ['OPEN', 'SOCCORSO'].includes(rowData.status) && userData.id === rowData.userId - ? __('Valuta', 'gepafin') - : __('Mostra', 'gepafin'); - return -
    diff --git a/src/pages/DomandaEditPreInstructor/index.js b/src/pages/DomandaEditPreInstructor/index.js index b516229..1a0c253 100644 --- a/src/pages/DomandaEditPreInstructor/index.js +++ b/src/pages/DomandaEditPreInstructor/index.js @@ -668,13 +668,15 @@ const DomandaEditPreInstructor = () => {

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

    - shouldDisableField(name) || evaluationShouldBeBlocked(data)} - name="files" - ndg={data.ndg} - applicationId={id}/> + {!isEmpty(data.files) + ? shouldDisableField(name) || evaluationShouldBeBlocked(data)} + name="files" + ndg={data.ndg} + applicationId={id}/> + :

    {__('Nessun documento allegato', 'gepafin')}

    }
    diff --git a/src/pages/DomandeBeneficiario/index.js b/src/pages/DomandeBeneficiario/index.js index f820b21..519beb0 100644 --- a/src/pages/DomandeBeneficiario/index.js +++ b/src/pages/DomandeBeneficiario/index.js @@ -8,7 +8,7 @@ import { useStore } from '../../store'; // components import BeneficiarioDomandeTable from './components/BeneficiarioDomandeTable'; -const DomandePreInstructor = () => { +const DomandeBeneficiario = () => { const chosenCompanyId = useStore().main.chosenCompanyId(); const companies = useStore().main.companies(); const company = head(companies.filter(o => o.id === chosenCompanyId)); @@ -29,4 +29,4 @@ const DomandePreInstructor = () => { ) } -export default DomandePreInstructor; \ No newline at end of file +export default DomandeBeneficiario; \ No newline at end of file diff --git a/src/pages/SoccorsoIstruttorioInstructorManager/index.js b/src/pages/SoccorsoIstruttorioInstructorManager/index.js new file mode 100644 index 0000000..c4ef886 --- /dev/null +++ b/src/pages/SoccorsoIstruttorioInstructorManager/index.js @@ -0,0 +1,95 @@ +import React, { useEffect, useState } from 'react'; +import { __ } from '@wordpress/i18n'; + +// components +import DashboardService from '../../service/dashboard-service'; +import { pathOr } from 'ramda'; +import NumberFlow from '@number-flow/react'; +import PreInstructorSoccorsiTable from '../SoccorsoIstruttorioPreInstructor/components/PreInstructorSoccorsiTable'; + +const SoccorsoIstruttorioInstructorManager = () => { + const [mainStats, setMainStats] = useState({}); + + const getStats = (data) => { + if (data.status === 'SUCCESS') { + setMainStats(data.data); + } + } + + const errGetStats = () => {} + + const getStatValue = (key, fallback = '') => { + return pathOr(fallback, [key], mainStats); + } + + useEffect(() => { + DashboardService.getAmendmentsStats(getStats, errGetStats); + }, []); + + return( +
    +
    +

    {__('Soccorso istruttorio panoramica', 'gepafin')}

    +
    + +
    + +
    +

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

    +
    +
    + {__('Totale richieste', 'gepafin')} + +
    +
    + {__('In attesa risposta', 'gepafin')} + +
    +
    + {__('Risposte ricevute', 'gepafin')} + +
    +
    + {__('Tempo medio di risposta', 'gepafin')} + +
    +
    + {__('Scadute', 'gepafin')} + +
    +
    + {__('Richieste in scadenza (48h)', 'gepafin')} + +
    +
    +
    + +
    + +
    + +
    +
    + ) +} + +export default SoccorsoIstruttorioInstructorManager; \ No newline at end of file diff --git a/src/pages/SoccorsoIstruttorioMioInstructorManager/components/InstructorManagerSoccorsiTable/index.js b/src/pages/SoccorsoIstruttorioMioInstructorManager/components/InstructorManagerSoccorsiTable/index.js new file mode 100644 index 0000000..5bbb927 --- /dev/null +++ b/src/pages/SoccorsoIstruttorioMioInstructorManager/components/InstructorManagerSoccorsiTable/index.js @@ -0,0 +1,175 @@ +import React, { useState, useEffect} from 'react'; +import { __ } from '@wordpress/i18n'; +import { is, uniq } from 'ramda'; +import { Link } from 'react-router-dom'; + +// store +import { storeGet } from '../../../../store'; + +// api +import AmendmentsService from '../../../../service/amendments-service'; + +// tools +import getBandoLabel from '../../../../helpers/getBandoLabel'; +import getBandoSeverity from '../../../../helpers/getBandoSeverity'; + +// components +import { FilterMatchMode, FilterOperator } from 'primereact/api'; +import { DataTable } from 'primereact/datatable'; +import { Column } from 'primereact/column'; +import { Button } from 'primereact/button'; +import { Calendar } from 'primereact/calendar'; +import ProperBandoLabel from '../../../../components/ProperBandoLabel'; +import { Dropdown } from 'primereact/dropdown'; +import { Tag } from 'primereact/tag'; + +import translationStrings from '../../../../translationStringsForComponents'; + + +const InstructorManagerSoccorsiTable = () => { + const [items, setItems] = useState(null); + const [filters, setFilters] = useState(null); + const [localAsyncRequest, setLocalAsyncRequest] = useState(false); + const [statuses, setStatuses] = useState([]); + + useEffect(() => { + const userData = storeGet.main.userData(); + setLocalAsyncRequest(true); + AmendmentsService.getSoccorsi(getCallback, errGetCallbacks, [ + ['userId', userData.id] + ]); + }, []); + + const getCallback = (data) => { + if (data.status === 'SUCCESS') { + setItems(getFormattedData(data.data)); + setStatuses(uniq(data.data.map(o => o.status))) + initFilters(); + } + setLocalAsyncRequest(false); + } + + const errGetCallbacks = (data) => { + setLocalAsyncRequest(false); + } + + const getFormattedData = (data) => { + return data.map((d) => { + d.startDate = is(String, d.startDate) ? new Date(d.startDate) : (d.startDate ? d.startDate : ''); + d.evaluationEndDate = is(String, d.evaluationEndDate) ? new Date(d.evaluationEndDate) : (d.evaluationEndDate ? d.evaluationEndDate : ''); + return d; + }); + }; + + const formatDate = (value) => { + return value.toLocaleDateString('it-IT', { + day: '2-digit', + month: '2-digit', + year: 'numeric' + }); + }; + + const clearFilter = () => { + initFilters(); + }; + + const initFilters = () => { + setFilters({ + global: { value: null, matchMode: FilterMatchMode.CONTAINS }, + callName: { + operator: FilterOperator.AND, + constraints: [{ value: null, matchMode: FilterMatchMode.STARTS_WITH }] + }, + companyName: { + operator: FilterOperator.AND, + constraints: [{ value: null, matchMode: FilterMatchMode.STARTS_WITH }] + }, + startDate: { + operator: FilterOperator.AND, + constraints: [{ value: null, matchMode: FilterMatchMode.DATE_IS }] + }, + evaluationEndDate: { + operator: FilterOperator.AND, + constraints: [{ value: null, matchMode: FilterMatchMode.DATE_IS }] + } + }); + }; + + const renderHeader = () => { + return ( +
    +
    + ); + }; + + const dateStartBodyTemplate = (rowData) => { + return formatDate(rowData.startDate); + }; + + const dateExpirationBodyTemplate = (rowData) => { + return rowData.evaluationEndDate ? formatDate(rowData.evaluationEndDate) : ''; + }; + + 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 + : null} {isCallExpired - ?

    {__('È scaduto', 'gepafin')}

    : null} - {isCallExpired || (!isEmpty(chosenCompanyId) && chosenCompanyId !== 0 && data.confidi + ?

    : null} + {isCallScheduled + ?

    : null} + {isCallExpired || isCallScheduled || (!isEmpty(chosenCompanyId) && chosenCompanyId !== 0 && data.confidi && (data.id !== 6 || (data.id === 6 && REACT_APP_HUB_ID !== 'p4lk3bcx1RStqTaIVVbXs'))) ?