- updated login page;

- updated registration page;
- created profile page;
- created company profile page;
This commit is contained in:
Vitalii Kiiko
2024-09-27 09:09:48 +02:00
parent 1bd00def4b
commit 478af11cb9
11 changed files with 406 additions and 41 deletions

View File

@@ -1,3 +1,3 @@
REACT_APP_TAB_TITLE=Gepafin REACT_APP_TAB_TITLE=Gepafin
REACT_APP_API_EXECUTION_ADDRESS=http://bandi.gepafin.it/v1 REACT_APP_API_EXECUTION_ADDRESS=http://bandi-api.gepafin.it/v1
REACT_APP_LOGO_FILENAME=logo.svg REACT_APP_LOGO_FILENAME=logo.svg

View File

@@ -41,6 +41,10 @@
background: var(--button-secondary-borderColor); background: var(--button-secondary-borderColor);
} }
} }
input[disabled], div.p-disabled {
background-color: #e3e3e3;
}
} }
.appForm__fieldItem { .appForm__fieldItem {

View File

@@ -1,5 +1,6 @@
import React, { useRef } from 'react'; import React, { useRef } from 'react';
import { __ } from '@wordpress/i18n'; import { __ } from '@wordpress/i18n';
import { useNavigate } from 'react-router-dom';
// store // store
import { storeSet, useTrackedStore } from '../../store'; import { storeSet, useTrackedStore } from '../../store';
@@ -9,6 +10,7 @@ import { Menu } from 'primereact/menu';
import { Avatar } from 'primereact/avatar'; import { Avatar } from 'primereact/avatar';
const TopBarProfileMenu = ({ menuLeftRef }) => { const TopBarProfileMenu = ({ menuLeftRef }) => {
const navigate = useNavigate();
const userData = useTrackedStore().main.userData(); const userData = useTrackedStore().main.userData();
const fulleName = `${userData.firstName} ${userData.lastName}`; const fulleName = `${userData.firstName} ${userData.lastName}`;
@@ -26,12 +28,18 @@ const TopBarProfileMenu = ({ menuLeftRef }) => {
); );
} }
}, },
/*{ {
label: __('Il mio profilo', 'gepafin'), label: __('Il mio profilo', 'gepafin'),
command: () => { command: () => {
console.log('go to profile page') navigate('/profilo')
} }
},*/ },
{
label: __('Profilo aziendale', 'gepafin'),
command: () => {
navigate('/profilo-aziendale')
}
},
{ {
separator: true separator: true
}, },

View File

@@ -379,7 +379,7 @@ const BandoApplication = () => {
iconPos="right"/> : null} iconPos="right"/> : null}
<Button <Button
disabled={completedSteps === 0 || completedSteps !== totalSteps} disabled={completedSteps === 0 || completedSteps !== totalSteps}
label={__('Invio', 'gepafin')} label={__('Invia', 'gepafin')}
icon="pi pi-check" icon="pi pi-check"
iconPos="right"/> iconPos="right"/>
</div> </div>

View File

@@ -1,5 +1,5 @@
import React, { useRef, useEffect } from 'react'; import React, { useRef, useEffect, useState } from 'react';
import { __ } from '@wordpress/i18n'; import { __, sprintf } from '@wordpress/i18n';
import { classNames } from 'primereact/utils'; import { classNames } from 'primereact/utils';
import { isEmpty } from 'ramda'; import { isEmpty } from 'ramda';
@@ -9,17 +9,50 @@ import { storeSet, useStore } from '../../store';
// components // components
import LogoIcon from '../../icons/LogoIcon'; import LogoIcon from '../../icons/LogoIcon';
import { Messages } from 'primereact/messages'; import { Messages } from 'primereact/messages';
import { useParams } from 'react-router-dom'; import { useSearchParams } from 'react-router-dom';
import AuthenticationService from '../../service/authentication-service';
const API_BASE_URL = process.env.REACT_APP_API_EXECUTION_ADDRESS; const API_BASE_URL = process.env.REACT_APP_API_EXECUTION_ADDRESS;
const Login = () => { const Login = () => {
const token = useStore().main.token(); const token = useStore().main.token();
const errorMsgs = useRef(null); const errorMsgs = useRef(null);
let { temp_token } = useParams(); const [loading, setLoading] = useState(false);
let [searchParams] = useSearchParams();
const loginWithSpid = () => { const loginWithSpid = () => {
window.location.replace(`${API_BASE_URL}/saml2/authenticate/loginumbria`); if (!loading) {
window.location.replace(`${API_BASE_URL}/saml2/authenticate/loginumbria`);
}
}
const validateCallback = (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 validateError = (err) => {
errorMsgs.current.show([
{
sticky: true, severity: 'error', summary: '',
detail: sprintf(__('%s', 'gepafin'), err.message),
closable: true
}
]);
setLoading(false);
} }
useEffect(() => { useEffect(() => {
@@ -29,8 +62,12 @@ const Login = () => {
}, [token]); }, [token]);
useEffect(() => { useEffect(() => {
console.log('temp_token', temp_token) const temp_token = searchParams.get('temp_token');
}, [temp_token]); if (temp_token) {
errorMsgs.current.clear();
AuthenticationService.validateExistingUser(temp_token, validateCallback, validateError);
}
}, [searchParams]);
return ( return (
<div className={classNames(['appPage', 'appPageLogin'])}> <div className={classNames(['appPage', 'appPageLogin'])}>

View File

@@ -1,7 +1,5 @@
import React, { useState, useEffect, useRef } from 'react'; import React, { useMemo, useRef } from 'react';
import { __, sprintf } from '@wordpress/i18n'; import { __ } from '@wordpress/i18n';
import { useNavigate, useParams } from 'react-router-dom';
import { is } from 'ramda';
// store // store
import { storeSet, useStore } from '../../store'; import { storeSet, useStore } from '../../store';
@@ -9,41 +7,190 @@ import { storeSet, useStore } from '../../store';
// components // components
import { Messages } from 'primereact/messages'; import { Messages } from 'primereact/messages';
import set404FromErrorResponse from '../../helpers/set404FromErrorResponse'; import set404FromErrorResponse from '../../helpers/set404FromErrorResponse';
import FormField from '../../components/FormField';
import { Button } from 'primereact/button';
import { useForm } from 'react-hook-form';
// api
import UserService from '../../service/user-service';
import getDateFromISOstring from '../../helpers/getDateFromISOstring';
const Profile = () => { const Profile = () => {
const isAsyncRequest = useStore().main.isAsyncRequest(); const isAsyncRequest = useStore().main.isAsyncRequest();
const { id } = useParams(); const userData = useStore().main.userData();
const [data, setData] = useState({});
const infoMsgs = useRef(null); const infoMsgs = useRef(null);
const getCallback = (data) => { const {
control,
handleSubmit,
formState: { errors },
setValue
} = useForm({
defaultValues: useMemo(() => {
return userData;
}, [userData]),
mode: 'onChange'
});
const onSubmit = (formData) => {
infoMsgs.current.clear();
storeSet.main.setAsyncRequest();
UserService.updateUser(formData, updateCallback, updateError);
};
const updateCallback = (data) => {
if (data.status === 'SUCCESS') { if (data.status === 'SUCCESS') {
//setData(getFormattedBandiData(data.data)); //setData(getFormattedBandiData(data.data));
} }
storeSet.main.unsetAsyncRequest(); storeSet.main.unsetAsyncRequest();
} }
const errGetCallback = (data) => { const updateError = (data) => {
set404FromErrorResponse(data); set404FromErrorResponse(data);
storeSet.main.unsetAsyncRequest(); storeSet.main.unsetAsyncRequest();
} }
useEffect(() => {
//BandoService.getBando(bandoId, getCallback, errGetCallback);
}, [id]);
return ( return (
<div className="appPage"> <div className="appPage">
<div className="appPage__pageHeader"> <div className="appPage__pageHeader">
<h1>{data.name}</h1> <h1>{__('Profilo utente', 'gepafin')}</h1>
<p>
{__('Profilo utente', 'gepafin')}
</p>
</div> </div>
<div className="appPage__spacer"></div> <div className="appPage__spacer"></div>
<Messages ref={infoMsgs}/> <Messages ref={infoMsgs}/>
<form className="appForm" onSubmit={handleSubmit(onSubmit)}>
<div className="appPageSection">
<h2>{__('Informazioni personali', 'gepafin')}</h2>
<div className="appForm__cols">
<FormField
type="textinput"
disabled={true}
fieldName="firstName"
label={__('Nome', 'gepafin')}
control={control}
errors={errors}
config={{ required: __('È obbligatorio', 'gepafin') }}
placeholder="Francesco"
/>
<FormField
type="textinput"
disabled={true}
fieldName="lastName"
label={__('Cognome', 'gepafin')}
control={control}
errors={errors}
config={{ required: __('È obbligatorio', 'gepafin') }}
placeholder="Moli"
/>
<FormField
type="textinput"
disabled={true}
fieldName="codiceFiscale"
label={__('Codice fiscale', 'gepafin')}
control={control}
errors={errors}
config={{ required: __('È obbligatorio', 'gepafin') }}
placeholder="XXXXXXXX"
/>
</div>
<div className="appForm__cols">
<FormField
type="textinput"
fieldName="email"
label={__('Email', 'gepafin')}
control={control}
errors={errors}
config={{ required: __('È obbligatorio', 'gepafin') }}
placeholder="user@example.com"
/>
<FormField
type="textinput"
fieldName="phoneNumber"
label={__('Telefono', 'gepafin')}
control={control}
errors={errors}
config={{ required: __('È obbligatorio', 'gepafin') }}
placeholder="1234567"
/>
</div>
</div>
<div className="appPageSection">
<h2>{__('Utenti Associati', 'gepafin')}</h2>
<div className="appForm__cols">
<FormField
type="select"
disabled={true}
fieldName="language"
defaultValue={'it'}
label={__('Lingua predefinita', 'gepafin')}
control={control}
errors={errors}
config={{ required: __('È obbligatorio', 'gepafin') }}
options={[
{ label: __('Italiano', 'gepafin'), name: 'it' }
]}
/>
<FormField
type="select"
disabled={true}
fieldName="timezone"
defaultValue={'Europe/Rome'}
label={__('Fuso Orario', 'gepafin')}
control={control}
errors={errors}
config={{ required: __('È obbligatorio', 'gepafin') }}
options={[
{ label: __('Europe/Rome', 'gepafin'), name: 'Europe/Rome' }
]}
/>
<FormField
type="select"
disabled={true}
fieldName="dateformat"
defaultValue={'DD/MM/YY'}
label={__('Formato Data', 'gepafin')}
control={control}
errors={errors}
config={{ required: __('È obbligatorio', 'gepafin') }}
options={[
{ label: __('DD/MM/YY', 'gepafin'), name: 'DD/MM/YY' }
]}
/>
</div>
</div>
<div className="appPageSection">
<h2>{__('Sicurezza', 'gepafin')}</h2>
<div className="appForm__row">
<label>{__('Ultimo accesso', 'gepafin')}</label>
<span>{getDateFromISOstring(userData.lastLogin)}</span>
</div>
</div>
<div className="appPage__spacer"></div>
<div className="appPageSection__hr">
<span>{__('Azioni rapide', 'gepafin')}</span>
</div>
<div className="appPageSection">
<div className="appPageSection__actions">
<Button
disabled={isAsyncRequest}
label={__('Salva modifiche', 'gepafin')}
icon="pi pi-check" iconPos="right"/>
</div>
</div>
</form>
</div> </div>
) )

View File

@@ -0,0 +1,111 @@
import React, { useMemo, useRef } from 'react';
import { __ } from '@wordpress/i18n';
// store
import { storeSet, useStore } from '../../store';
// components
import { Messages } from 'primereact/messages';
import set404FromErrorResponse from '../../helpers/set404FromErrorResponse';
import FormField from '../../components/FormField';
import { Button } from 'primereact/button';
import { useForm } from 'react-hook-form';
// api
import UserService from '../../service/user-service';
import getDateFromISOstring from '../../helpers/getDateFromISOstring';
const ProfileCompany = () => {
const isAsyncRequest = useStore().main.isAsyncRequest();
const userData = useStore().main.userData();
const infoMsgs = useRef(null);
const {
control,
handleSubmit,
formState: { errors },
setValue
} = useForm({
defaultValues: useMemo(() => {
return userData;
}, [userData]),
mode: 'onChange'
});
const onSubmit = (formData) => {
infoMsgs.current.clear();
storeSet.main.setAsyncRequest();
UserService.updateUser(formData, updateCallback, updateError);
};
const updateCallback = (data) => {
if (data.status === 'SUCCESS') {
//setData(getFormattedBandiData(data.data));
}
storeSet.main.unsetAsyncRequest();
}
const updateError = (data) => {
set404FromErrorResponse(data);
storeSet.main.unsetAsyncRequest();
}
return (
<div className="appPage">
<div className="appPage__pageHeader">
<h1>{__('Profilo aziendale', 'gepafin')}</h1>
</div>
<div className="appPage__spacer"></div>
<Messages ref={infoMsgs}/>
<form className="appForm" onSubmit={handleSubmit(onSubmit)}>
<div className="appPageSection">
<h2>{__('Informazioni aziendali', 'gepafin')}</h2>
<div className="appForm__cols">
<FormField
type="textinput"
fieldName="organization"
label={__('Ragione Sociale', 'gepafin')}
control={control}
errors={errors}
config={{ required: __('È obbligatorio', 'gepafin') }}
placeholder="Azienda"
/>
</div>
<div className="appForm__cols">
<FormField
type="textinput"
fieldName="address"
label={__('Indirizzo Sede Legale', 'gepafin')}
control={control}
errors={errors}
config={{ required: __('È obbligatorio', 'gepafin') }}
/>
</div>
</div>
<div className="appPage__spacer"></div>
<div className="appPageSection__hr">
<span>{__('Azioni rapide', 'gepafin')}</span>
</div>
<div className="appPageSection">
<div className="appPageSection__actions">
<Button
disabled={isAsyncRequest}
label={__('Salva modifiche', 'gepafin')}
icon="pi pi-check" iconPos="right"/>
</div>
</div>
</form>
</div>
)
}
export default ProfileCompany;

View File

@@ -3,7 +3,7 @@ import { __, sprintf } from '@wordpress/i18n';
import { useForm } from 'react-hook-form'; import { useForm } from 'react-hook-form';
import { classNames } from 'primereact/utils'; import { classNames } from 'primereact/utils';
import { isEmpty } from 'ramda'; import { isEmpty } from 'ramda';
import { useParams } from 'react-router-dom'; import { useSearchParams } from 'react-router-dom';
// api // api
import AuthenticationService from '../../service/authentication-service'; import AuthenticationService from '../../service/authentication-service';
@@ -24,19 +24,19 @@ const Registration = () => {
const token = useStore().main.token(); const token = useStore().main.token();
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const errorMsgs = useRef(null); const errorMsgs = useRef(null);
let { temp_token } = useParams(); let [searchParams] = useSearchParams();
const { const {
control, control,
handleSubmit, handleSubmit,
formState: { errors }, formState: { errors },
setValue
} = useForm({ mode: 'onChange' }); } = useForm({ mode: 'onChange' });
const onSubmit = (formData) => { const onSubmit = (formData) => {
errorMsgs.current.clear(); errorMsgs.current.clear();
//setLoading(true); setLoading(true);
console.log('formData', formData, errors);
//AuthenticationService.login(request, regCallback, regError); AuthenticationService.registerUser(formData, regCallback, regError);
}; };
const regCallback = (data) => { const regCallback = (data) => {
@@ -49,7 +49,7 @@ const Registration = () => {
errorMsgs.current.show([ errorMsgs.current.show([
{ {
sticky: true, severity: 'error', summary: '', sticky: true, severity: 'error', summary: '',
detail: data.message, detail: data.data.join(', '),
closable: true closable: true
} }
]); ]);
@@ -58,6 +58,35 @@ const Registration = () => {
} }
const regError = (err) => { const regError = (err) => {
errorMsgs.current.show([
{
sticky: true, severity: 'error', summary: '',
detail: sprintf(__('%s', 'gepafin'), err.message),
closable: true
}
]);
setLoading(false);
}
const validateCallback = (data) => {
if (data.status === 'SUCCESS') {
const { codiceFiscale, firstName, lastName } = data.data;
setValue('codiceFiscale', codiceFiscale);
setValue('firstName', firstName);
setValue('lastName', lastName);
} else {
errorMsgs.current.show([
{
sticky: true, severity: 'error', summary: '',
detail: data.message,
closable: true
}
]);
}
setLoading(false);
}
const validateError = (err) => {
errorMsgs.current.show([ errorMsgs.current.show([
{ {
sticky: true, severity: 'error', summary: '', sticky: true, severity: 'error', summary: '',
@@ -76,8 +105,11 @@ const Registration = () => {
}, [token]); }, [token]);
useEffect(() => { useEffect(() => {
console.log('temp_token', temp_token) const temp_token = searchParams.get('temp_token');
}, [temp_token]); if (temp_token) {
AuthenticationService.validateNewUser(temp_token, validateCallback, validateError);
}
}, [searchParams]);
return ( return (
<div className={classNames(['appPage', 'appPageLogin'])}> <div className={classNames(['appPage', 'appPageLogin'])}>
@@ -95,7 +127,8 @@ const Registration = () => {
<div className="appForm__cols"> <div className="appForm__cols">
<FormField <FormField
type="textinput" type="textinput"
fieldName="first_name" disabled={true}
fieldName="firstName"
label={__('Nome', 'gepafin')} label={__('Nome', 'gepafin')}
control={control} control={control}
errors={errors} errors={errors}
@@ -105,7 +138,8 @@ const Registration = () => {
<FormField <FormField
type="textinput" type="textinput"
fieldName="second_name" disabled={true}
fieldName="lastName"
label={__('Cognome', 'gepafin')} label={__('Cognome', 'gepafin')}
control={control} control={control}
errors={errors} errors={errors}
@@ -117,7 +151,8 @@ const Registration = () => {
<div className="appForm__cols"> <div className="appForm__cols">
<FormField <FormField
type="textinput" type="textinput"
fieldName="fiscal_code" disabled={true}
fieldName="codiceFiscale"
label={__('Codice fiscale', 'gepafin')} label={__('Codice fiscale', 'gepafin')}
control={control} control={control}
errors={errors} errors={errors}
@@ -132,7 +167,7 @@ const Registration = () => {
<FormField <FormField
type="datepicker" type="datepicker"
fieldName="birth_date" fieldName="birthDate"
label={__('Data di nascita', 'gepafin')} label={__('Data di nascita', 'gepafin')}
control={control} control={control}
errors={errors} errors={errors}
@@ -159,7 +194,7 @@ const Registration = () => {
<FormField <FormField
type="textinput" type="textinput"
fieldName="phone" fieldName="phoneNumber"
label={__('Telefono', 'gepafin')} label={__('Telefono', 'gepafin')}
control={control} control={control}
errors={errors} errors={errors}

View File

@@ -21,6 +21,7 @@ import Registration from './pages/Registration';
import BandiBeneficiario from './pages/BandiBeneficiario'; import BandiBeneficiario from './pages/BandiBeneficiario';
import LoginAdmin from './pages/LoginAdmin'; import LoginAdmin from './pages/LoginAdmin';
import Profile from './pages/Profile'; import Profile from './pages/Profile';
import ProfileCompany from './pages/ProfileCompany';
const routes = ({ role }) => { const routes = ({ role }) => {
return ( return (
@@ -74,6 +75,10 @@ const routes = ({ role }) => {
{'ROLE_SUPER_ADMIN' === role ? <PageNotFound/> : null} {'ROLE_SUPER_ADMIN' === role ? <PageNotFound/> : null}
{'ROLE_BENEFICIARY' === role ? <Profile/> : null} {'ROLE_BENEFICIARY' === role ? <Profile/> : null}
</DefaultLayout>}/> </DefaultLayout>}/>
<Route path="/profilo-aziendale" element={<DefaultLayout>
{'ROLE_SUPER_ADMIN' === role ? <PageNotFound/> : null}
{'ROLE_BENEFICIARY' === role ? <ProfileCompany/> : null}
</DefaultLayout>}/>
</Route> </Route>
<Route exact path="/login" element={<Login/>}/> <Route exact path="/login" element={<Login/>}/>
<Route exact path="/loginAdmin" element={<LoginAdmin/>}/> <Route exact path="/loginAdmin" element={<LoginAdmin/>}/>

View File

@@ -72,4 +72,12 @@ export default class AuthenticationService {
static me = (callback, errCallback) => { static me = (callback, errCallback) => {
NetworkService.get(`${API_BASE_URL}/user/me`, callback, errCallback); NetworkService.get(`${API_BASE_URL}/user/me`, callback, errCallback);
}; };
static validateNewUser = (token, callback, errCallback) => {
NetworkService.get(`${API_BASE_URL}/user/sso/validate/new-user/${token}`, callback, errCallback);
};
static validateExistingUser = (token, callback, errCallback) => {
NetworkService.get(`${API_BASE_URL}/user/sso/validate/existing-user/${token}`, callback, errCallback);
};
} }

View File

@@ -0,0 +1,10 @@
import { NetworkService } from './network-service';
const API_BASE_URL = process.env.REACT_APP_API_EXECUTION_ADDRESS;
export default class UserService {
static updateUser = (id, body, callback, errCallback) => {
NetworkService.put(`${API_BASE_URL}/user/${id}`, body, callback, errCallback);
};
}