- added login and loginAdmin;

- fixed bug with saving application data;
This commit is contained in:
Vitalii Kiiko
2024-09-25 16:25:09 +02:00
parent b008fcd37a
commit 25b1b5ae7d
9 changed files with 226 additions and 112 deletions

View File

@@ -1,3 +1,3 @@
REACT_APP_TAB_TITLE=Gepafin REACT_APP_TAB_TITLE=Gepafin
REACT_APP_API_EXECUTION_ADDRESS=https://api-dev-gepafin.memento.credit/v1 REACT_APP_API_EXECUTION_ADDRESS=http://79.137.88.15/v1
REACT_APP_LOGO_FILENAME=logo.svg REACT_APP_LOGO_FILENAME=logo.svg

View File

@@ -1,8 +1,8 @@
import React, { useState, useEffect, useCallback, useRef } from 'react'; import React, { useState, useEffect, useCallback, useRef } from 'react';
import { __, sprintf } from '@wordpress/i18n'; import { __, sprintf } from '@wordpress/i18n';
import { useNavigate, useParams } from 'react-router-dom'; import { useParams } from 'react-router-dom';
import { klona } from 'klona'; import { klona } from 'klona';
import { head, range, is, isNil } from 'ramda'; import { head, range, is, pluck } from 'ramda';
import { useForm } from 'react-hook-form'; import { useForm } from 'react-hook-form';
// store // store
@@ -40,6 +40,7 @@ const BandoApplication = () => {
const [bandoTitle, setBandoTitle] = useState(''); const [bandoTitle, setBandoTitle] = useState('');
const [formId, setFormId] = useState(''); const [formId, setFormId] = useState('');
const [totalSteps, setTotalSteps] = useState(0); const [totalSteps, setTotalSteps] = useState(0);
const [completedSteps, setCompletedSteps] = useState(0);
const [activeStep, setActiveStep] = useState(1); const [activeStep, setActiveStep] = useState(1);
const [stepItems, setStepItems] = useState([{ label: 'Passo' }]); const [stepItems, setStepItems] = useState([{ label: 'Passo' }]);
const isAsyncRequest = useStore().main.isAsyncRequest(); const isAsyncRequest = useStore().main.isAsyncRequest();
@@ -67,12 +68,52 @@ const BandoApplication = () => {
} }
const onSubmit = () => { const onSubmit = () => {
const applId = getApplicationId();
storeSet.main.setAsyncRequest();
ApplicationService.updateStatusApplication(applId, {}, submitApplicationCallback, errSubmitApplicationCallback, [
['status', 'SUBMIT']
]);
}; };
const saveDraft = useCallback(() => { const submitApplicationCallback = (data) => {
if (data.status === 'SUCCESS') {
if (toast.current) {
toast.current.show({
severity: 'success',
summary: '',
detail: __('La domanda è stata presentata!', 'gepafin')
});
}
}
storeSet.main.unsetAsyncRequest();
}
const errSubmitApplicationCallback = (data) => {
storeSet.main.unsetAsyncRequest();
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 {
set404FromErrorResponse(data);
}
}
const saveDraft = () => {
trigger(); trigger();
const formValues = getValues(); const formValues = getValues();
const newFormValues = Object.keys(formValues).reduce((acc, cur) => { const usedFieldsIds = pluck('id', formData);
const newFormValues = Object.keys(formValues)
.filter(v => usedFieldsIds.includes(v))
.reduce((acc, cur) => {
const formField = head(formData.filter(o => o.id === cur)); const formField = head(formData.filter(o => o.id === cur));
let fieldVal = formValues[cur]; let fieldVal = formValues[cur];
@@ -103,32 +144,33 @@ const BandoApplication = () => {
formMsgs.current.clear(); formMsgs.current.clear();
} }
ApplicationService.submitForm(applId, submitData, submitFormCallback, errSubmitFormCallback, [ ApplicationService.saveDraft(applId, submitData, saveDraftCallback, errSaveDraftCallback, [
['formId', formId] ['formId', formId]
]); ]);
} }
}, [errors]); }
const getApplicationId = () => { const getApplicationId = () => {
const parsed = parseInt(id) const parsed = parseInt(id)
return !isNaN(parsed) ? parsed : 0; return !isNaN(parsed) ? parsed : 0;
} }
const submitFormCallback = (data) => { const saveDraftCallback = (data) => {
if (data.status === 'SUCCESS') { if (data.status === 'SUCCESS') {
console.log(data.data);
if (toast.current) { if (toast.current) {
toast.current.show({ toast.current.show({
severity: 'success', severity: 'success',
summary: '', summary: '',
detail: __('Saved!', 'gepafin') detail: __('Salvato!', 'gepafin')
}); });
} }
// update info about application completeness
ApplicationService.getApplicationForm(data.data.id, getStatusCheckCallback, errGetStatusCheckCallbacks);
} }
storeSet.main.unsetAsyncRequest(); storeSet.main.unsetAsyncRequest();
} }
const errSubmitFormCallback = (data) => { const errSaveDraftCallback = (data) => {
storeSet.main.unsetAsyncRequest(); storeSet.main.unsetAsyncRequest();
if (data.status === 'VALIDATION_ERROR') { if (data.status === 'VALIDATION_ERROR') {
if (formMsgs.current) { if (formMsgs.current) {
@@ -183,7 +225,8 @@ const BandoApplication = () => {
setFormId(data.data.formId); setFormId(data.data.formId);
setTotalSteps(data.data.totalFormSteps); setTotalSteps(data.data.totalFormSteps);
setActiveStep(data.data.currentStep) setCompletedSteps(data.data.completedSteps);
setActiveStep(data.data.currentStep);
} }
storeSet.main.unsetAsyncRequest(); storeSet.main.unsetAsyncRequest();
} }
@@ -203,9 +246,18 @@ const BandoApplication = () => {
} }
} }
const getStatusCheckCallback = (data) => {
if (data.status === 'SUCCESS') {
setCompletedSteps(data.data.completedSteps);
}
}
const errGetStatusCheckCallbacks = (data) => {
set404FromErrorResponse(data);
}
useEffect(() => { useEffect(() => {
const newFormData = klona(formInitialData); const newFormData = klona(formInitialData);
console.log('newFormData', newFormData);
newFormData.map(o => setValue(o.fieldId, o.fieldValue)); newFormData.map(o => setValue(o.fieldId, o.fieldValue));
}, [formInitialData]); }, [formInitialData]);
@@ -325,6 +377,11 @@ const BandoApplication = () => {
label={__('Vai avanti', 'gepafin')} label={__('Vai avanti', 'gepafin')}
icon="pi pi-arrow-right" icon="pi pi-arrow-right"
iconPos="right"/> : null} iconPos="right"/> : null}
<Button
disabled={completedSteps === 0 || completedSteps !== totalSteps}
label={__('Invio', 'gepafin')}
icon="pi pi-check"
iconPos="right"/>
</div> </div>
</div> </div>
</form> </form>

View File

@@ -145,6 +145,10 @@ const BandoFlowEdit = () => {
setInitialForm(data.data.initialForm); setInitialForm(data.data.initialForm);
setFinalForm(data.data.finalForm); setFinalForm(data.data.finalForm);
setBandoStatus(data.data.callStatus); setBandoStatus(data.data.callStatus);
const chosenFieldItem = head(data.data.flowData.filter(o => !isEmpty(o.chosenField)));
if (chosenFieldItem) {
setMainField(chosenFieldItem.chosenField);
}
const flowDataItem = head(data.data.flowData.filter(o => !isEmpty(o.chosenField))); const flowDataItem = head(data.data.flowData.filter(o => !isEmpty(o.chosenField)));
if (flowDataItem) { if (flowDataItem) {

View File

@@ -1,7 +1,7 @@
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect } from 'react';
import { __ } from '@wordpress/i18n'; import { __ } from '@wordpress/i18n';
import { useParams, useNavigate } from 'react-router-dom'; import { useParams, useNavigate } from 'react-router-dom';
import { isEmpty } from 'ramda'; import { is, isEmpty, isNil } from 'ramda';
import { classNames } from 'primereact/utils'; import { classNames } from 'primereact/utils';
// components // components
@@ -14,6 +14,7 @@ import FormsService from '../../service/forms-service';
// store // store
import { storeSet } from '../../store'; import { storeSet } from '../../store';
import set404FromErrorResponse from '../../helpers/set404FromErrorResponse'; import set404FromErrorResponse from '../../helpers/set404FromErrorResponse';
import BandoService from '../../service/bando-service';
const BandoForms = () => { const BandoForms = () => {
const { id } = useParams(); const { id } = useParams();
@@ -22,6 +23,7 @@ const BandoForms = () => {
const [selectedTemplate, setSelectedTemplate] = useState(null); const [selectedTemplate, setSelectedTemplate] = useState(null);
const [selectedForm, setSelectedForm] = useState(null); const [selectedForm, setSelectedForm] = useState(null);
const [forms, setForms] = useState([]); const [forms, setForms] = useState([]);
const [bandoStatus, setBandoStatus] = useState('');
const doCreateNewForm = () => { const doCreateNewForm = () => {
navigate(`/bandi/${id}/forms/new`); navigate(`/bandi/${id}/forms/new`);
@@ -46,6 +48,18 @@ const BandoForms = () => {
//navigate(`/bandi/${id}`); //navigate(`/bandi/${id}`);
} }
const getCallback = (data) => {
if (data.status === 'SUCCESS') {
setBandoStatus(data.data.status);
}
storeSet.main.unsetAsyncRequest();
}
const errGetCallback = (data) => {
set404FromErrorResponse(data);
storeSet.main.unsetAsyncRequest();
}
const getFormsCallback = (data) => { const getFormsCallback = (data) => {
if (data.status === 'SUCCESS') { if (data.status === 'SUCCESS') {
const forms = data.data.map(o => ({label: o.label, value: o.id})) const forms = data.data.map(o => ({label: o.label, value: o.id}))
@@ -68,6 +82,7 @@ const BandoForms = () => {
]) ])
storeSet.main.setAsyncRequest(); storeSet.main.setAsyncRequest();
BandoService.getBando(id, getCallback, errGetCallback);
FormsService.getFormsForCall(bandoId, getFormsCallback, errGetFormsCallback); FormsService.getFormsForCall(bandoId, getFormsCallback, errGetFormsCallback);
}, [id]); }, [id]);
@@ -112,6 +127,7 @@ const BandoForms = () => {
<p>{__('Inizia con un form completamente vuoto e personalizzabil', 'gepafin')}</p> <p>{__('Inizia con un form completamente vuoto e personalizzabil', 'gepafin')}</p>
<Button <Button
type="button" type="button"
disabled={'PUBLISH' === bandoStatus}
onClick={doCreateNewForm} onClick={doCreateNewForm}
label={__('Crea form', 'gepafin')}/> label={__('Crea form', 'gepafin')}/>
</div> </div>
@@ -134,7 +150,7 @@ const BandoForms = () => {
outlined outlined
disabled={!selectedForm} disabled={!selectedForm}
onClick={goToEditForm} onClick={goToEditForm}
label={__('Modifica', 'gepafin')} label={'PUBLISH' === bandoStatus ? __('Mostra', 'gepafin') : __('Modifica', 'gepafin')}
icon="pi pi-cog" iconPos="right"/> icon="pi pi-cog" iconPos="right"/>
</div> </div>
</div> </div>

View File

@@ -55,10 +55,8 @@ const MyLatestSubmissionsTable = () => {
const getFormattedBandiData = (data) => { const getFormattedBandiData = (data) => {
return [...(data || [])].map((d) => { return [...(data || [])].map((d) => {
d.modify_date = new Date(); d.callEndDate = new Date(d.callEndDate);
d.end_date = new Date(); d.modifiedDate = new Date(d.modifiedDate);
d.name = `Bando ${d.callId}`;
d.progress = 37;
return d; return d;
}); });
@@ -89,9 +87,9 @@ const MyLatestSubmissionsTable = () => {
const initFilters = () => { const initFilters = () => {
setFilters({ setFilters({
global: { value: null, matchMode: FilterMatchMode.CONTAINS }, global: { value: null, matchMode: FilterMatchMode.CONTAINS },
name: { operator: FilterOperator.AND, constraints: [{ value: null, matchMode: FilterMatchMode.STARTS_WITH }] }, callTitle: { operator: FilterOperator.AND, constraints: [{ value: null, matchMode: FilterMatchMode.STARTS_WITH }] },
modify_date: { operator: FilterOperator.AND, constraints: [{ value: null, matchMode: FilterMatchMode.DATE_IS }] }, modifiedDate: { operator: FilterOperator.AND, constraints: [{ value: null, matchMode: FilterMatchMode.DATE_IS }] },
end_date: { operator: FilterOperator.AND, constraints: [{ value: null, matchMode: FilterMatchMode.DATE_IS }] }, callEndDate: { operator: FilterOperator.AND, constraints: [{ value: null, matchMode: FilterMatchMode.DATE_IS }] },
status: { operator: FilterOperator.OR, constraints: [{ value: null, matchMode: FilterMatchMode.EQUALS }] }, status: { operator: FilterOperator.OR, constraints: [{ value: null, matchMode: FilterMatchMode.EQUALS }] },
}); });
setGlobalFilterValue(''); setGlobalFilterValue('');
@@ -110,11 +108,11 @@ const MyLatestSubmissionsTable = () => {
}; };
const dateModifyBodyTemplate = (rowData) => { const dateModifyBodyTemplate = (rowData) => {
return formatDate(rowData.modify_date); return formatDate(rowData.modifiedDate);
}; };
const dateEndBodyTemplate = (rowData) => { const dateEndBodyTemplate = (rowData) => {
return formatDate(rowData.end_date); return formatDate(rowData.callEndDate);
}; };
const dateFilterTemplate = (options) => { const dateFilterTemplate = (options) => {
@@ -152,12 +150,12 @@ const MyLatestSubmissionsTable = () => {
globalFilterFields={['name', 'status']} globalFilterFields={['name', 'status']}
header={header} header={header}
emptyMessage="Nothing found." onFilter={(e) => setFilters(e.filters)}> emptyMessage="Nothing found." onFilter={(e) => setFilters(e.filters)}>
<Column field="name" header={__('Bando', 'gepafin')} filter filterPlaceholder="Search by name" <Column field="callTitle" header={__('Bando', 'gepafin')} filter filterPlaceholder="Search by name"
style={{ minWidth: '12rem' }}/> style={{ minWidth: '12rem' }}/>
<Column header={__('Scadenza', 'gepafin')} filterField="end_date" dataType="date" <Column header={__('Scadenza', 'gepafin')} filterField="callEndDate" dataType="date"
style={{ minWidth: '10rem' }} style={{ minWidth: '10rem' }}
body={dateEndBodyTemplate} filter filterElement={dateFilterTemplate}/> body={dateEndBodyTemplate} filter filterElement={dateFilterTemplate}/>
<Column header={__('Ultima modifica', 'gepafin')} filterField="modify_date" dataType="date" <Column header={__('Ultima modifica', 'gepafin')} filterField="modifiedDate" dataType="date"
style={{ minWidth: '10rem' }} style={{ minWidth: '10rem' }}
body={dateModifyBodyTemplate} filter filterElement={dateFilterTemplate}/> body={dateModifyBodyTemplate} filter filterElement={dateFilterTemplate}/>
<Column field="status" header={__('Stato', 'gepafin')} filterMenuStyle={{ width: '14rem' }} <Column field="status" header={__('Stato', 'gepafin')} filterMenuStyle={{ width: '14rem' }}

View File

@@ -1,66 +1,18 @@
import React, { useRef, useState, useEffect } from 'react'; import React, { useRef, useEffect } from 'react';
import { __, sprintf } from '@wordpress/i18n'; import { __ } from '@wordpress/i18n';
import { useForm } from 'react-hook-form';
import { classNames } from 'primereact/utils'; import { classNames } from 'primereact/utils';
import { isEmpty } from 'ramda'; import { isEmpty } from 'ramda';
// tools
import AuthenticationService from '../../service/authentication-service';
// store // store
import { storeSet, useStore } from '../../store'; import { storeSet, useStore } from '../../store';
// components // components
import FormField from '../../components/FormField';
import LogoIcon from '../../icons/LogoIcon'; import LogoIcon from '../../icons/LogoIcon';
import { Button } from 'primereact/button';
import { Messages } from 'primereact/messages'; import { Messages } from 'primereact/messages';
const Login = () => { const Login = () => {
const token = useStore().main.token(); const token = useStore().main.token();
const [loading, setLoading] = useState(false);
const errorMsgs = useRef(null); const errorMsgs = useRef(null);
const {
control,
handleSubmit,
formState: { errors },
} = useForm({ mode: 'onChange' });
const onSubmit = (formData) => {
errorMsgs.current.clear();
setLoading(true);
const request = {
...formData,
rememberMe: true
}
AuthenticationService.login(request, loginCallback, loginError);
};
const loginCallback = (data) => {
if (data.status === 'SUCCESS') {
storeSet.main.setAuthData({
token: data.data.token,
userData: data.data.user
});
} else {
errorMsgs.current.show([
{ sticky: true, severity: 'error', summary: '',
detail: data.message,
closable: true }
]);
}
setLoading(false);
}
const loginError = (err) => {
errorMsgs.current.show([
{ sticky: true, severity: 'error', summary: '',
detail: sprintf(__('%s', 'gepafin'), err),
closable: true }
]);
setLoading(false);
}
const loginWithSpid = () => { const loginWithSpid = () => {
console.log('spid') console.log('spid')
@@ -68,7 +20,6 @@ const Login = () => {
useEffect(() => { useEffect(() => {
if (!isEmpty(token)) { if (!isEmpty(token)) {
setLoading(true);
window.location.replace('/') window.location.replace('/')
} }
}, [token]); }, [token]);
@@ -106,39 +57,6 @@ const Login = () => {
<span>{__('Entra con SPID', 'gepafin')}</span> <span>{__('Entra con SPID', 'gepafin')}</span>
</button> </button>
<div className="appPage__spacer"></div>
<div className="appPageSection__hr">
<span>{__('Oppure', 'gepafin')}</span>
</div>
<div className="appPage__spacer"></div>
<form className="appForm" onSubmit={handleSubmit(onSubmit)}>
<FormField
type="textinput"
fieldName="email"
label={__('Email', 'gepafin')}
control={control}
errors={errors}
config={{ required: __('È obbligatorio', 'gepafin') }}
placeholder="sample@example.com"
/>
<FormField
type="textinput"
inputtype="password"
fieldName="password"
label={__('Password', 'gepafin')}
control={control}
errors={errors}
config={{ required: __('È obbligatorio', 'gepafin') }}
/>
<Button
label={__('Accedi', 'gepafin')}
disabled={loading}/>
</form>
</div> </div>
</div> </div>
) )

View File

@@ -0,0 +1,115 @@
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 } from 'ramda';
// tools
import AuthenticationService from '../../service/authentication-service';
// store
import { storeSet, useStore } from '../../store';
// components
import FormField from '../../components/FormField';
import LogoIcon from '../../icons/LogoIcon';
import { Button } from 'primereact/button';
import { Messages } from 'primereact/messages';
const LoginAdmin = () => {
const token = useStore().main.token();
const [loading, setLoading] = useState(false);
const errorMsgs = useRef(null);
const {
control,
handleSubmit,
formState: { errors },
} = useForm({ mode: 'onChange' });
const onSubmit = (formData) => {
errorMsgs.current.clear();
setLoading(true);
const request = {
...formData,
rememberMe: true
}
AuthenticationService.login(request, loginCallback, loginError);
};
const loginCallback = (data) => {
if (data.status === 'SUCCESS') {
storeSet.main.setAuthData({
token: data.data.token,
userData: data.data.user
});
} else {
errorMsgs.current.show([
{ sticky: true, severity: 'error', summary: '',
detail: data.message,
closable: true }
]);
}
setLoading(false);
}
const loginError = (err) => {
errorMsgs.current.show([
{ sticky: true, severity: 'error', summary: '',
detail: sprintf(__('%s', 'gepafin'), err),
closable: true }
]);
setLoading(false);
}
useEffect(() => {
if (!isEmpty(token)) {
setLoading(true);
window.location.replace('/')
}
}, [token]);
return (
<div className={classNames(['appPage', 'appPageLogin'])}>
<div className="appPageLogin__wrapper">
<LogoIcon/>
<h1>{__('Accedi o Registrati', 'gepafin')}</h1>
<div className="appPage__spacer"></div>
<Messages ref={errorMsgs}/>
<div className="appPage__spacer"></div>
<form className="appForm" onSubmit={handleSubmit(onSubmit)}>
<FormField
type="textinput"
fieldName="email"
label={__('Email', 'gepafin')}
control={control}
errors={errors}
config={{ required: __('È obbligatorio', 'gepafin') }}
placeholder="sample@example.com"
/>
<FormField
type="textinput"
inputtype="password"
fieldName="password"
label={__('Password', 'gepafin')}
control={control}
errors={errors}
config={{ required: __('È obbligatorio', 'gepafin') }}
/>
<Button
label={__('Accedi', 'gepafin')}
disabled={loading}/>
</form>
</div>
</div>
)
}
export default LoginAdmin;

View File

@@ -19,6 +19,7 @@ import Applications from './pages/Applications';
import BandoApplication from './pages/BandoApplication'; import BandoApplication from './pages/BandoApplication';
import Registration from './pages/Registration'; import Registration from './pages/Registration';
import BandiBeneficiario from './pages/BandiBeneficiario'; import BandiBeneficiario from './pages/BandiBeneficiario';
import LoginAdmin from './pages/LoginAdmin';
const routes = ({ role }) => { const routes = ({ role }) => {
return ( return (
@@ -70,6 +71,7 @@ const routes = ({ role }) => {
</DefaultLayout>}/> </DefaultLayout>}/>
</Route> </Route>
<Route exact path="/login" element={<Login/>}/> <Route exact path="/login" element={<Login/>}/>
<Route exact path="/loginAdmin" element={<LoginAdmin/>}/>
<Route exact path="/registration" element={<Registration/>}/> <Route exact path="/registration" element={<Registration/>}/>
{/*<Route exact path="/forgot-password" element={<ForgotPassword/>}/>*/} {/*<Route exact path="/forgot-password" element={<ForgotPassword/>}/>*/}
<Route path="*" element={<PageNotFound/>}/> <Route path="*" element={<PageNotFound/>}/>

View File

@@ -20,7 +20,11 @@ export default class ApplicationService {
NetworkService.post(`${API_BASE_URL}/application/call/${id}`, body, callback, errCallback); NetworkService.post(`${API_BASE_URL}/application/call/${id}`, body, callback, errCallback);
}; };
static submitForm = (id, body, callback, errCallback, queryParams) => { static saveDraft = (id, body, callback, errCallback, queryParams) => {
NetworkService.put(`${API_BASE_URL}/application/${id}`, body, callback, errCallback, queryParams); NetworkService.put(`${API_BASE_URL}/application/${id}`, body, callback, errCallback, queryParams);
}; };
static updateStatusApplication = (id, body, callback, errCallback, queryParams) => {
NetworkService.put(`${API_BASE_URL}/application/${id}/status`, body, callback, errCallback, queryParams);
};
} }