Compare commits

..

10 Commits

Author SHA1 Message Date
Vitalii Kiiko
563b6190ab - updated prod env variables; 2026-04-14 11:57:05 +02:00
Vitalii Kiiko
3a023df7ea - removed technical evaluation status from change status tool; 2026-04-14 11:16:02 +02:00
Vitalii Kiiko
9d9c8ad723 - form field label setting is reuqired when at least one validation setting is defined for a form field; 2026-04-13 15:31:31 +02:00
Vitalii Kiiko
dae8fea9f7 - fixed freezing form; 2026-04-13 12:30:28 +02:00
Vitalii Kiiko
ba16a67776 Merge branch 'master' into develop 2026-04-10 16:48:17 +02:00
Vitalii Kiiko
84d87d3e33 - #6423 - Show applications APPROVED, REJECTED in table 'change status'; 2026-04-10 16:47:56 +02:00
Vitalii Kiiko
9b630ff9be Merge branch 'master' into develop 2026-04-09 15:04:06 +02:00
Vitalii Kiiko
eadbb913d5 - removed button for sviluppumria; 2026-04-09 14:57:09 +02:00
Vitalii Kiiko
19dba4202d - re enabled spreadsheet field; 2026-04-09 10:28:26 +02:00
Vitalii Kiiko
b10004a1fe - updated copies and filtered out spreadsheet for reg forms; 2026-04-09 09:45:56 +02:00
14 changed files with 101 additions and 42 deletions

View File

@@ -1,6 +1,6 @@
REACT_APP_TAB_TITLE=Gepafin
REACT_APP_API_EXECUTION_ADDRESS=https://bandi-api.gepafin.it/v1
REACT_APP_API_ADMIN_BASE_URL=https://admin-gepafin-dev-be.bflows.ai
REACT_APP_API_ADMIN_BASE_URL=https://admin-gepafin-be.bflows.ai
REACT_APP_API_ADDRESS=https://bandi-api.gepafin.it
REACT_APP_API_ADDRESS_WS=https://bandi-api.gepafin.it/wss
REACT_APP_LOGO_FILENAME=gepafin-logo.svg

View File

@@ -1,10 +1,30 @@
import React from 'react';
import React, { useMemo } from 'react';
import { classNames } from 'primereact/utils';
import { Controller } from 'react-hook-form';
import { is, isEmpty } from 'ramda';
import { Calendar } from 'primereact/calendar';
const DatepickerField = ({ field, fieldState, disabled, timeOnly, minDate, maxDate }) => {
const dateValue = useMemo(
() => is(String, field.value) && !isEmpty(field.value) ? new Date(field.value) : field.value,
[field.value]
);
return (
<Calendar id={field.name}
disabled={disabled}
value={dateValue}
onChange={(e) => field.onChange(e.value)}
dateFormat="dd/mm/yy"
hourFormat="24"
timeOnly={timeOnly}
showIcon
minDate={minDate}
maxDate={maxDate}
className={classNames({ 'p-invalid': fieldState.invalid })}/>
);
};
const Datepicker = ({
fieldName,
label,
@@ -28,17 +48,15 @@ const Datepicker = ({
control={control}
defaultValue={defaultValue}
rules={config}
render={({ field, fieldState }) => (<Calendar id={field.name}
render={({ field, fieldState }) => (
<DatepickerField
field={field}
fieldState={fieldState}
disabled={disabled}
value={is(String, field.value) && !isEmpty(field.value) ? new Date(field.value) : field.value}
onChange={(e) => field.onChange(e.value)}
dateFormat="dd/mm/yy"
hourFormat="24"
timeOnly={timeOnly}
showIcon
minDate={minDate}
maxDate={maxDate}
className={classNames({ 'p-invalid': fieldState.invalid })}/>
/>
)}/>
{infoText ? <small>{infoText}</small> : null}
</>)

View File

@@ -1,4 +1,4 @@
import React from 'react';
import React, { useMemo } from 'react';
import { classNames } from 'primereact/utils';
import { isNil } from 'ramda';
@@ -18,9 +18,12 @@ const DatepickerRange = ({
maxDate = null,
disabled = false
}) => {
const datesDefaultValue = !isNil(defaultValue) && defaultValue.length
const datesDefaultValue = useMemo(
() => !isNil(defaultValue) && defaultValue.length
? defaultValue.map(v => new Date(v))
: [];
: [],
[defaultValue]
);
return (
<>
<label htmlFor={fieldName} className={classNames({ 'p-error': errors[fieldName] })}>

View File

@@ -39,8 +39,8 @@ const NumberInput = ({
readOnly={readOnly}
value={is(Number, field.value) || (!is(Number, field.value) && !isNaN(parseFloat(field.value))) ? field.value : ''}
onValueChange={(e) => field.onChange(e.value)}
min={minAttr}
max={maxAttr}
min={readOnly ? undefined : minAttr}
max={readOnly ? undefined : maxAttr}
mode="decimal"
locale={locale}
showButtons

View File

@@ -24,12 +24,12 @@ import { Toast } from 'primereact/toast';
const allStatuses = [
'SUBMIT', 'EVALUATION', 'SOCCORSO', 'APPOINTMENT', 'NDG', 'ADMISSIBLE',
'AWAITING_TECHNICAL_EVALUATION', 'TECHNICAL_EVALUATION'
'AWAITING_TECHNICAL_EVALUATION', 'APPROVED', 'REJECTED'
];
const availableStatuses = [
'EVALUATION', 'SOCCORSO', 'APPOINTMENT', 'NDG', 'ADMISSIBLE',
'AWAITING_TECHNICAL_EVALUATION', 'TECHNICAL_EVALUATION'
'AWAITING_TECHNICAL_EVALUATION', 'APPROVED', 'REJECTED'
];
const ManageApplStatusSection = () => {

View File

@@ -27,15 +27,15 @@ const navItems = [
{
permission: ['ROOT_MANAGE_AMENDMENT_REOPEN'],
route: '/admin/amendment-reopen',
label: __('Riapri Integrazione', 'gepafin'),
subtitle: __('Riapri un\'integrazione chiusa', 'gepafin'),
label: __('Riapri Soccorso', 'gepafin'),
subtitle: __('Riapri un soccorso chiuso', 'gepafin'),
icon: 'pi pi-refresh'
},
{
permission: ['ROOT_MANAGE_AMENDMENT_EXTEND'],
route: '/admin/amendment-extend',
label: __('Estendi Integrazione', 'gepafin'),
subtitle: __('Estendi la scadenza di un\'integrazione', 'gepafin'),
label: __('Estendi Soccorso', 'gepafin'),
subtitle: __('Estendi la scadenza di un soccorso', 'gepafin'),
icon: 'pi pi-calendar-plus'
},
{

View File

@@ -106,6 +106,7 @@ const BandoApplication = () => {
const values = getValues();
const formValues = watch();
const onValidate = () => {
saveDraft('VALIDATE');
};

View File

@@ -26,7 +26,7 @@ import getTokens from '../../../../../../helpers/getTokens';
const ElementSettingSpreadsheet = React.lazy(() => import('../ElementSettingSpreadsheet'));
const ElementSetting = ({ setting, changeFn, updateDataFn, bandoStatus, context }) => {
const ElementSetting = ({ setting, changeFn, updateDataFn, bandoStatus, context, required, hasError }) => {
const [existingVars, setExistingVars] = useState([]);
const documentCategories = useStoreValue('documentCategories');
@@ -176,6 +176,7 @@ const ElementSetting = ({ setting, changeFn, updateDataFn, bandoStatus, context
</>
} else {
return <InputText id={setting.name} aria-describedby={`${setting.name}-help`}
className={hasError ? 'p-invalid' : undefined}
value={setting.value}
onChange={(e) => changeFn(e.target.value, setting.name)}/>
}
@@ -199,8 +200,14 @@ const ElementSetting = ({ setting, changeFn, updateDataFn, bandoStatus, context
}, []);
return <div className="formElementSettings__field" key={setting.name}>
<label htmlFor={setting.name}>{settingLabels[setting.name]}</label>
<label htmlFor={setting.name}>
{settingLabels[setting.name]}
{required ? <span style={{ color: 'red', marginLeft: '4px' }}>*</span> : null}
</label>
{getProperField(setting)}
{setting.name === 'label' && hasError
? <small className="p-error">{__('Label è obbligatoria quando è impostata una validazione.', 'gepafin')}</small>
: null}
{setting.name === 'formula' && !isEmpty(existingVars)
? <div className="formElementSettings__fieldVarsList">
<p>Existing variables: {existingVars.map(v => <code key={v}>{`{${v}}`}</code>)}</p>

View File

@@ -29,6 +29,7 @@ const BuilderElementSettings = ({ closeSettingsFn, callStatus, context }) => {
const [validators, setValidators] = useState({});
const [dynamicData, setDynamicData] = useState([]);
const [criteria, setCriteria] = useState([]);
const [labelError, setLabelError] = useState(false);
const numberBasedValidatorFields = ['min', 'max', 'minLength', 'maxLength'];
const customValidationOptions = [
{ value: 'isPIVA', label: 'isPIVA' },
@@ -44,7 +45,18 @@ const BuilderElementSettings = ({ closeSettingsFn, callStatus, context }) => {
{ value: 'nonEmptyTables', label: 'nonEmptyTables' }
]
const hasAnyValidation = !isEmpty(validators) && (
validators.isRequired === true ||
Object.entries(validators)
.filter(([k]) => k !== 'isRequired')
.some(([, v]) => !isNil(v))
);
const onChange = (value, name) => {
if (name === 'label' && labelError) {
setLabelError(false);
}
const newSettings = settings
.map(o => {
if (o.name === name) {
@@ -73,11 +85,21 @@ const BuilderElementSettings = ({ closeSettingsFn, callStatus, context }) => {
}
const saveSettings = () => {
const latestSettings = storeGet('chosenFieldSettings') || settings;
if (hasAnyValidation) {
const labelSetting = head(latestSettings.filter(o => o.name === 'label'));
if (labelSetting && isEmpty(labelSetting.value.trim())) {
setLabelError(true);
return;
}
}
setLabelError(false);
let newActiveElementData = klona(activeElementData);
// Prefer the store value over React state: child components (e.g. ElementSettingSpreadsheet)
// write to the store synchronously, but React state updates are batched and may lag behind
// when saveSettings is called in the same event as the last setDataFn call.
const latestSettings = storeGet('chosenFieldSettings') || settings;
newActiveElementData = wrap(newActiveElementData).set(['settings'], latestSettings).value();
newActiveElementData = wrap(newActiveElementData).set(['validators'], validators).value();
newActiveElementData = wrap(newActiveElementData).set(['dynamicData'], dynamicData).value();
@@ -167,7 +189,9 @@ const BuilderElementSettings = ({ closeSettingsFn, callStatus, context }) => {
context={context}
callStatus={callStatus}
changeFn={onChange}
updateDataFn={onUpdateOptions}/>)
updateDataFn={onUpdateOptions}
required={o.name === 'label' && hasAnyValidation}
hasError={o.name === 'label' && labelError && hasAnyValidation}/>)
: null}
{!isNil(dynamicDataOptions[activeElementData.name]) && context === 'application'
? <div className="formElementSettings__field">

View File

@@ -27,7 +27,7 @@ import BandoService from '../../service/bando-service';
import DocumentCategoryService from '../../service/document-category-service';
// TODO temp data
import { elementItems } from '../../tempData';
//import { elementItems } from '../../tempData';
const BandoFormsEdit = () => {
const { id, formId } = useParams();
@@ -224,7 +224,7 @@ const BandoFormsEdit = () => {
.filter(o => o.id !== 22)
.sort((a, b) => a.sortOrder - b.sortOrder));*/
storeSet('elementItems', data.data
.filter(o => o.id !== 22)
//.filter(o => o.id !== 22 && o.name !== 'spreadsheet')
.sort((a, b) => a.sortOrder - b.sortOrder));
}
storeSet('unsetAsyncRequest');

View File

@@ -1,4 +1,4 @@
import React, { useCallback, useEffect, useState } from 'react';
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { __ } from '@wordpress/i18n';
import { classNames } from 'primereact/utils';
import { wrap } from 'object-path-immutable';
@@ -32,9 +32,11 @@ const DocumentsBeneficiary = () => {
const [newFileData, setNewFileData] = useState({});
const [fileAttached, setFileAttached] = useState([]);
const [reloadHash, setReloadHash] = useState(0);
const today = new Date();
const tomorrow = new Date(today);
tomorrow.setDate(today.getDate() + 1);
const tomorrow = useMemo(() => {
const d = new Date();
d.setDate(d.getDate() + 1);
return d;
}, []);
const company = head(companies.filter(o => o.id === chosenCompanyId));
const onCreateNew = useCallback((type) => {

View File

@@ -1212,7 +1212,8 @@ const DomandaEditInstructorManager = () => {
onClick={doMakeAdmisible}
label={__('Ammissibile formalmente', 'gepafin')}
/>}
{APP_HUB_ID === 't7jh5wfg9QXylNaTZkPoE'
{/* Removed for Sviluppumbria */}
{/*{APP_HUB_ID === 't7jh5wfg9QXylNaTZkPoE'
? <Button
type="button"
disabled={!data.id || !['ADMISSIBLE'].includes(data.applicationStatus) || evaluationBlockedForUser(data) || connectedSoccorsoId !== 0}
@@ -1223,7 +1224,7 @@ const DomandaEditInstructorManager = () => {
: __('Punteggio non sufficiente per passaggio alla valutazione tecnica ed economico finanziaria', 'gepafin')}
severity={isAdmissible ? 'success' : 'warning'}
label={__('Valutazione tecnico-finanziaria positiva', 'gepafin')}
/> : null}
/> : null}*/}
{APP_HUB_ID !== 't7jh5wfg9QXylNaTZkPoE' && data.applicationStatus === 'AWAITING_TECHNICAL_EVALUATION'
? <Button
type="button"

View File

@@ -49,9 +49,11 @@ const EvaluationExtraFiles = ({
const [addDocLoading, setAddDocLoading] = useState(false);
const [reloadHash, setReloadHash] = useState(0);
const toast = useRef(null);
const today = new Date();
const tomorrow = new Date(today);
tomorrow.setDate(today.getDate() + 1);
const tomorrow = useMemo(() => {
const d = new Date();
d.setDate(d.getDate() + 1);
return d;
}, []);
const {
control,
handleSubmit,

View File

@@ -1119,7 +1119,8 @@ const DomandaEditPreInstructor = () => {
onClick={doMakeAdmisible}
label={__('Ammissibile formalmente', 'gepafin')}
/>}
{APP_HUB_ID === 't7jh5wfg9QXylNaTZkPoE'
{/* Removed for Sviluppumbria */}
{/*{APP_HUB_ID === 't7jh5wfg9QXylNaTZkPoE'
? <Button
type="button"
disabled={!data.id || !['ADMISSIBLE'].includes(data.applicationStatus) || evaluationBlockedForUser(data) || connectedSoccorsoId !== 0}
@@ -1130,7 +1131,7 @@ const DomandaEditPreInstructor = () => {
: __('Punteggio non sufficiente per passaggio alla valutazione tecnica ed economico finanziaria', 'gepafin')}
severity={isAdmissible ? 'success' : 'warning'}
label={__('Valutazione tecnico-finanziaria positiva', 'gepafin')}
/> : null}
/> : null}*/}
{APP_HUB_ID !== 't7jh5wfg9QXylNaTZkPoE' && data.applicationStatus === 'AWAITING_TECHNICAL_EVALUATION'
? <Button
type="button"