dquote> - updated registartion page, added gdpr and marketing checkboxes;

dquote> - fixed number input;
dquote> - updated latest submissions table component;
This commit is contained in:
Vitalii Kiiko
2024-10-07 11:06:35 +02:00
parent c687bec8d5
commit ad42ed6326
9 changed files with 254 additions and 78 deletions

View File

@@ -9,12 +9,20 @@
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
width: 100%; width: 100%;
max-width: 540px; max-width: 600px;
padding: 24px; padding: 24px;
.appForm { .appForm {
width: 100%; width: 100%;
} }
.p-panel-content {
font-size: 14px;
.appForm__col {
gap: 1em;
}
}
} }
.appPageLogin__spidBtn.appPageLogin__spidBtn { .appPageLogin__spidBtn.appPageLogin__spidBtn {
@@ -38,3 +46,17 @@
line-height: 15px; line-height: 15px;
} }
} }
.reverseSwitchField {
gap: 1em;
.appForm__field.switch {
width: auto;
> .appForm__row {
display: flex;
justify-content: flex-end;
flex-direction: row-reverse;
}
}
}

View File

@@ -91,7 +91,7 @@
padding: 0.5rem; padding: 0.5rem;
} }
.p-message-detail, .p-progressbar-label { .p-message-detail.p-message-detail, .p-progressbar-label.p-progressbar-label {
color: white; color: white;
} }

View File

@@ -22,7 +22,6 @@ const NumberInput = ({
disabled = false, disabled = false,
useGrouping = false useGrouping = false
}) => { }) => {
console.log('defaultValue', defaultValue);
const input = <Controller const input = <Controller
name={fieldName} name={fieldName}
control={control} control={control}

View File

@@ -1,9 +1,10 @@
export const mimeTypes = [ export const mimeTypes = [
{ name: 'PDF', code: 'application/pdf' }, { name: 'PDF', code: 'application/pdf' },
{ name: 'Firmato PDF o Word', code: 'application/pkcs7-mime' },
{ name: 'ZIP', code: 'application/zip' }, { name: 'ZIP', code: 'application/zip' },
{ name: 'Immagine', code: 'image/*' }, { name: 'Immagine', code: 'image/*' },
{ {
name: 'Word doc', name: 'Word',
code: 'application/msword,application/vnd.openxmlformats-officedocument.wordprocessingml.document' code: 'application/msword,application/vnd.openxmlformats-officedocument.wordprocessingml.document'
}, },
{ name: 'Excel doc', code: 'application/vnd.ms-excel' } { name: 'Excel doc', code: 'application/vnd.ms-excel' }

View File

@@ -2,6 +2,9 @@ import { __ } from '@wordpress/i18n';
const getBandoLabel = (status) => { const getBandoLabel = (status) => {
switch (status) { switch (status) {
case 'SUBMIT':
return __('Inviato', 'gepafin');
case 'PUBLISH': case 'PUBLISH':
return __('Pubblicato', 'gepafin'); return __('Pubblicato', 'gepafin');

View File

@@ -1,5 +1,10 @@
import { __ } from '@wordpress/i18n';
const getBandoSeverity = (status) => { const getBandoSeverity = (status) => {
switch (status) { switch (status) {
case 'SUBMIT':
return 'success';
case 'PUBLISH': case 'PUBLISH':
return 'success'; return 'success';

View File

@@ -42,6 +42,7 @@ const BandoApplication = () => {
const [formId, setFormId] = useState(''); const [formId, setFormId] = useState('');
const [totalSteps, setTotalSteps] = useState(0); const [totalSteps, setTotalSteps] = useState(0);
const [completedSteps, setCompletedSteps] = useState(0); const [completedSteps, setCompletedSteps] = useState(0);
const [applicationStatus, setApplicationStatus] = useState('');
const [activeStep, setActiveStep] = useState(1); const [activeStep, setActiveStep] = useState(1);
const isAsyncRequest = useStore().main.isAsyncRequest(); const isAsyncRequest = useStore().main.isAsyncRequest();
const toast = useRef(null); const toast = useRef(null);
@@ -86,6 +87,9 @@ const BandoApplication = () => {
detail: __('La domanda è stata presentata!', 'gepafin') detail: __('La domanda è stata presentata!', 'gepafin')
}); });
} }
if (data.data.applicationStatus) {
setApplicationStatus(data.data.applicationStatus);
}
} }
storeSet.main.unsetAsyncRequest(); storeSet.main.unsetAsyncRequest();
} }
@@ -126,24 +130,24 @@ const BandoApplication = () => {
const newFormValues = Object.keys(formValues) const newFormValues = Object.keys(formValues)
.filter(v => usedFieldsIds.includes(v)) .filter(v => usedFieldsIds.includes(v))
.reduce((acc, cur) => { .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];
if (formValues[cur] && formValues[cur].toISOString) { if (formValues[cur] && formValues[cur].toISOString) {
const tzAwareDate = new TZDate(formValues[cur], 'Europe/Berlin'); const tzAwareDate = new TZDate(formValues[cur], 'Europe/Berlin');
fieldVal = tzAwareDate.toISOString().substring(0, 19); fieldVal = tzAwareDate.toISOString().substring(0, 19);
} }
fieldVal = !fieldVal ? null : fieldVal; fieldVal = !fieldVal ? null : fieldVal;
if (formField && formField.name === 'fileupload') { if (formField && formField.name === 'fileupload') {
fieldVal = is(Array, fieldVal) ? fieldVal.map(o => o.id).join(',') : fieldVal; fieldVal = is(Array, fieldVal) ? fieldVal.map(o => o.id).join(',') : fieldVal;
} }
acc.push({ acc.push({
'fieldId': cur, 'fieldId': cur,
'fieldValue': fieldVal 'fieldValue': fieldVal
}); });
return acc; return acc;
}, []); }, []);
const submitData = { const submitData = {
formFields: newFormValues formFields: newFormValues
} }
@@ -167,7 +171,8 @@ const BandoApplication = () => {
return !isNaN(parsed) ? parsed : 0; return !isNaN(parsed) ? parsed : 0;
} }
const saveDraftCallback = (data, saveAndMove) => { const saveDraftCallback = (data, saveAndMove = '') => {
storeSet.main.unsetAsyncRequest();
if (data.status === 'SUCCESS') { if (data.status === 'SUCCESS') {
if (toast.current) { if (toast.current) {
toast.current.show({ toast.current.show({
@@ -176,18 +181,16 @@ const BandoApplication = () => {
detail: __('Salvato!', 'gepafin') detail: __('Salvato!', 'gepafin')
}); });
} }
if (!isEmpty(saveAndMove)) { if (!isEmpty(saveAndMove) && is(String, saveAndMove)) {
storeSet.main.setAsyncRequest(); storeSet.main.setAsyncRequest();
ApplicationService.getApplicationForm(data.data.id, getApplFormCallback, errGetApplFormCallbacks, [ ApplicationService.getApplicationForm(data.data.id, getApplFormCallback, errGetApplFormCallbacks, [
['formId', formId], ['formId', formId],
['action', saveAndMove] ['action', saveAndMove]
]); ]);
} else { } else {
// update info about application completeness
ApplicationService.getApplicationForm(data.data.id, getStatusCheckCallback, errGetStatusCheckCallbacks); ApplicationService.getApplicationForm(data.data.id, getStatusCheckCallback, errGetStatusCheckCallbacks);
} }
} }
storeSet.main.unsetAsyncRequest();
} }
const errSaveDraftCallback = (data) => { const errSaveDraftCallback = (data) => {
@@ -221,26 +224,10 @@ const BandoApplication = () => {
const goBackward = () => { const goBackward = () => {
saveDraft('PREVIOUS'); saveDraft('PREVIOUS');
/*if (formId) {
const applId = getApplicationId();
storeSet.main.setAsyncRequest();
ApplicationService.getApplicationForm(applId, getApplFormCallback, errGetApplFormCallbacks, [
['formId', formId],
['action', 'PREVIOUS']
]);
}*/
} }
const goForward = () => { const goForward = () => {
saveDraft('NEXT'); saveDraft('NEXT');
/*if (formId) {
const applId = getApplicationId();
storeSet.main.setAsyncRequest();
ApplicationService.getApplicationForm(applId, getApplFormCallback, errGetApplFormCallbacks, [
['formId', formId],
['action', 'NEXT']
]);
}*/
} }
const getApplFormCallback = (data) => { const getApplFormCallback = (data) => {
@@ -250,6 +237,7 @@ const BandoApplication = () => {
setFormId(data.data.formId); setFormId(data.data.formId);
setTotalSteps(data.data.totalFormSteps); setTotalSteps(data.data.totalFormSteps);
setCompletedSteps(data.data.completedSteps); setCompletedSteps(data.data.completedSteps);
setApplicationStatus(data.data.applicationStatus)
setActiveStep(data.data.currentStep); setActiveStep(data.data.currentStep);
if (data.data.applicationFormResponse.formFields) { if (data.data.applicationFormResponse.formFields) {
@@ -361,24 +349,24 @@ const BandoApplication = () => {
return ['paragraph'].includes(o.name) && text return ['paragraph'].includes(o.name) && text
? <div className="appForm__content" key={o.id}>{renderHtmlContent(text.value)}</div> ? <div className="appForm__content" key={o.id}>{renderHtmlContent(text.value)}</div>
: <FormField : <FormField
key={o.id} key={o.id}
type={o.name} type={o.name}
fieldName={o.id} fieldName={o.id}
label={label ? label.value : ''} label={label ? label.value : ''}
placeholder={placeholder ? placeholder.value : ''} placeholder={placeholder ? placeholder.value : ''}
control={control} control={control}
register={register} register={register}
errors={errors} errors={errors}
defaultValue={values[o.id]} defaultValue={values[o.id]}
maxFractionDigits={step ? step.value : 0} maxFractionDigits={step ? step.value : 0}
accept={mimeValue} accept={mimeValue}
config={validations} config={validations}
options={options ? options.value : []} options={options ? options.value : []}
setDataFn={setValue} setDataFn={setValue}
sourceId={getApplicationId()} sourceId={getApplicationId()}
useGrouping={false} useGrouping={false}
tableColumns={tableColumns ? tableColumns.value : {}} tableColumns={tableColumns ? tableColumns.value : {}}
/> />
})} })}
<div className="appPage__spacer"></div> <div className="appPage__spacer"></div>
@@ -392,26 +380,28 @@ const BandoApplication = () => {
{activeStep > 1 && activeStep <= totalSteps {activeStep > 1 && activeStep <= totalSteps
? <Button ? <Button
type="button" type="button"
disabled={'SUBMIT' === applicationStatus}
onClick={goBackward} onClick={goBackward}
label={__('Vai indietro', 'gepafin')} label={__('Vai indietro', 'gepafin')}
icon="pi pi-arrow-left" icon="pi pi-arrow-left"
iconPos="left"/> : null} iconPos="left"/> : null}
<Button <Button
type="button" type="button"
disabled={isAsyncRequest} disabled={isAsyncRequest || 'SUBMIT' === applicationStatus}
onClick={saveDraft} onClick={saveDraft}
outlined outlined
label={__('Salva bozza', 'gepafin')} icon="pi pi-save" iconPos="right"/> label={__('Salva bozza', 'gepafin')} icon="pi pi-save" iconPos="right"/>
{activeStep < totalSteps {activeStep < totalSteps
//&& activeStep === completedSteps //&& activeStep === completedSteps
? <Button ? <Button
type="button" type="button"
disabled={'SUBMIT' === applicationStatus}
onClick={goForward} onClick={goForward}
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 <Button
disabled={completedSteps === 0 || completedSteps !== totalSteps} disabled={completedSteps === 0 || completedSteps !== totalSteps || 'SUBMIT' === applicationStatus}
label={__('Invia', 'gepafin')} label={__('Invia', 'gepafin')}
icon="pi pi-check" icon="pi pi-check"
iconPos="right"/> iconPos="right"/>

View File

@@ -1,4 +1,4 @@
import React, { useState, useEffect} from 'react'; import React, { useState, useEffect } from 'react';
import { __ } from '@wordpress/i18n'; import { __ } from '@wordpress/i18n';
import { uniq } from 'ramda'; import { uniq } from 'ramda';
@@ -39,7 +39,7 @@ const MyLatestSubmissionsTable = () => {
const getApplCallback = (data) => { const getApplCallback = (data) => {
if (data.status === 'SUCCESS') { if (data.status === 'SUCCESS') {
if(data.data.length) { if (data.data.length) {
setItems(getFormattedBandiData(data.data)); setItems(getFormattedBandiData(data.data));
setStatuses(uniq(items.map(o => o.status))) setStatuses(uniq(items.map(o => o.status)))
initFilters(); initFilters();
@@ -87,9 +87,18 @@ const MyLatestSubmissionsTable = () => {
const initFilters = () => { const initFilters = () => {
setFilters({ setFilters({
global: { value: null, matchMode: FilterMatchMode.CONTAINS }, global: { value: null, matchMode: FilterMatchMode.CONTAINS },
callTitle: { operator: FilterOperator.AND, constraints: [{ value: null, matchMode: FilterMatchMode.STARTS_WITH }] }, callTitle: {
modifiedDate: { operator: FilterOperator.AND, constraints: [{ value: null, matchMode: FilterMatchMode.DATE_IS }] }, operator: FilterOperator.AND,
callEndDate: { operator: FilterOperator.AND, constraints: [{ value: null, matchMode: FilterMatchMode.DATE_IS }] }, constraints: [{ value: null, matchMode: FilterMatchMode.STARTS_WITH }]
},
modifiedDate: {
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('');
@@ -98,10 +107,12 @@ const MyLatestSubmissionsTable = () => {
const renderHeader = () => { const renderHeader = () => {
return ( return (
<div className="appTableHeader"> <div className="appTableHeader">
<Button type="button" icon="pi pi-filter-slash" label={__('Pulisci', 'gepafin')} outlined onClick={clearFilter} /> <Button type="button" icon="pi pi-filter-slash" label={__('Pulisci', 'gepafin')} outlined
onClick={clearFilter}/>
<IconField iconPosition="left"> <IconField iconPosition="left">
<InputIcon className="pi pi-search" /> <InputIcon className="pi pi-search"/>
<InputText value={globalFilterValue} onChange={onGlobalFilterChange} placeholder={__('Cerca', 'gepafin')} /> <InputText value={globalFilterValue} onChange={onGlobalFilterChange}
placeholder={__('Cerca', 'gepafin')}/>
</IconField> </IconField>
</div> </div>
); );
@@ -116,7 +127,8 @@ const MyLatestSubmissionsTable = () => {
}; };
const dateFilterTemplate = (options) => { const dateFilterTemplate = (options) => {
return <Calendar value={options.value} onChange={(e) => options.filterCallback(e.value, options.index)} dateFormat="mm/dd/yy" placeholder="mm/dd/yyyy" mask="99/99/9999" />; return <Calendar value={options.value} onChange={(e) => options.filterCallback(e.value, options.index)}
dateFormat="mm/dd/yy" placeholder="mm/dd/yyyy" mask="99/99/9999"/>;
}; };
const statusBodyTemplate = (rowData) => { const statusBodyTemplate = (rowData) => {
@@ -124,7 +136,10 @@ const MyLatestSubmissionsTable = () => {
}; };
const statusFilterTemplate = (options) => { const statusFilterTemplate = (options) => {
return <Dropdown value={options.value} options={statuses} onChange={(e) => options.filterCallback(e.value, options.index)} itemTemplate={statusItemTemplate} placeholder="Select One" className="p-column-filter" showClear />; return <Dropdown value={options.value} options={statuses}
onChange={(e) => options.filterCallback(e.value, options.index)}
itemTemplate={statusItemTemplate} placeholder="Select One" className="p-column-filter"
showClear/>;
}; };
const progressBodyTemplate = (options) => { const progressBodyTemplate = (options) => {
@@ -132,18 +147,20 @@ const MyLatestSubmissionsTable = () => {
}; };
const statusItemTemplate = (option) => { const statusItemTemplate = (option) => {
return <Tag value={getBandoLabel(option)} severity={getBandoSeverity(option)} />; return <Tag value={getBandoLabel(option)} severity={getBandoSeverity(option)}/>;
}; };
const actionsBodyTemplate = (rowData) => { const actionsBodyTemplate = (rowData) => {
return <Link to={`/imieibandi/${rowData.id}`}> return 'DRAFT' === rowData.status
<Button severity="info" label={__('Modifica', 'gepafin')} icon="pi pi-pencil" size="small" iconPos="right" /> ? <Link to={`/imieibandi/${rowData.id}`}>
</Link> <Button severity="info" label={__('Modifica', 'gepafin')} icon="pi pi-pencil" size="small"
iconPos="right"/>
</Link> : null
} }
const header = renderHeader(); const header = renderHeader();
return( return (
<div className="appPageSection__table"> <div className="appPageSection__table">
<DataTable value={items} paginator showGridlines rows={10} loading={isAsyncRequest} dataKey="id" <DataTable value={items} paginator showGridlines rows={10} loading={isAsyncRequest} dataKey="id"
filters={filters} filters={filters}

View File

@@ -9,7 +9,7 @@ import { useSearchParams } from 'react-router-dom';
import AuthenticationService from '../../service/authentication-service'; import AuthenticationService from '../../service/authentication-service';
// tools // tools
import { isCodiceFiscale, isEmail } from '../../helpers/validators'; import { isEmail } from '../../helpers/validators';
// store // store
import { storeSet, useStore } from '../../store'; import { storeSet, useStore } from '../../store';
@@ -19,6 +19,7 @@ import FormField from '../../components/FormField';
import LogoIcon from '../../icons/LogoIcon'; import LogoIcon from '../../icons/LogoIcon';
import { Button } from 'primereact/button'; import { Button } from 'primereact/button';
import { Messages } from 'primereact/messages'; import { Messages } from 'primereact/messages';
import { Panel } from 'primereact/panel';
const Registration = () => { const Registration = () => {
const token = useStore().main.token(); const token = useStore().main.token();
@@ -29,8 +30,10 @@ const Registration = () => {
control, control,
handleSubmit, handleSubmit,
formState: { errors }, formState: { errors },
setValue setValue,
getValues
} = useForm({ mode: 'onChange' }); } = useForm({ mode: 'onChange' });
const values = getValues();
const onSubmit = (formData) => { const onSubmit = (formData) => {
errorMsgs.current.clear(); errorMsgs.current.clear();
@@ -100,6 +103,18 @@ const Registration = () => {
setLoading(false); setLoading(false);
} }
const disableAllChecks = () => {
setValue('marketing1', false);
setValue('marketing2', false);
setValue('marketing3', false);
}
const enableAllChecks = () => {
setValue('marketing1', true);
setValue('marketing2', true);
setValue('marketing3', true);
}
useEffect(() => { useEffect(() => {
if (!isEmpty(token)) { if (!isEmpty(token)) {
setLoading(true); setLoading(true);
@@ -207,6 +222,130 @@ const Registration = () => {
/> />
</div> </div>
<Panel
headerTemplate={(options) => {
return (
<div className={classNames(['p-panel-header', 'reverseSwitchField'])}>
<FormField
type="switch"
fieldName="privacy"
label={__('Ho letto e accetto lInformativa sulla Privacy (Necessario)', 'gepafin')}
control={control}
errors={errors}
defaultValue={values['privacy']}
onLabel={''}
offLabel={''}
config={{
required: __('È obbligatorio', 'gepafin'),
}}
/>
<div>
{options.togglerElement}
</div>
</div>
);
}}
toggleable>
<p className="m-0">
{__('Dichiaro di aver preso visione, prima dell\'accesso al portale https://bandi.gepafin.it, dell\' "Informativa Privacy" all\'interno dell\'Appendice 10 dell\'Avviso secondo il Regolamento UE 2016/679 relativo alla protezione delle persone fisiche con riguardo al trattamento dei dati personale, nonché alla libera circolazione di tali dati e che abroga la Direttiva 95/46 CE.', 'gepafin')}
</p>
</Panel>
<Panel
headerTemplate={(options) => {
return (
<div className={classNames(['p-panel-header', 'reverseSwitchField'])}>
<FormField
type="switch"
fieldName="terms"
label={__('Ho letto e accetto Termini e condizioni (Necessario)', 'gepafin')}
control={control}
errors={errors}
defaultValue={values['terms']}
onLabel={''}
offLabel={''}
config={{
required: __('È obbligatorio', 'gepafin'),
}}
/>
<div>
{options.togglerElement}
</div>
</div>
);
}}
toggleable>
<p className="m-0">
{__('Termini e condizioni', 'gepafin')}
</p>
</Panel>
<Panel
header={__('Altri consensi (facoltativo)', 'gepafin')}
toggleable>
<div className="appForm__col">
<div className={classNames(['appForm__row', 'reverseSwitchField'])}>
<FormField
type="switch"
fieldName="marketing"
label={''}
control={control}
errors={errors}
defaultValue={values['marketing']}
onLabel={''}
offLabel={''}
/>
<div>
{__('Invio di materiale pubblicitario, vendita diretta, compimento di ricerche di mercato o comunicazione commerciale riguardanti promozione e vendita di prodotti e servizi della Gepafin, mediante modalità di contatto automatizzate (posta elettronica, PEC, messaggi tramite canali informatici, network ed applicazioni web) e tradizionali (come posta cartacea e chiamate telefoniche con operatore)', 'gepafin')}
</div>
</div>
<div className={classNames(['appForm__row', 'reverseSwitchField'])}>
<FormField
type="switch"
fieldName="offers"
label={''}
control={control}
errors={errors}
defaultValue={values['offers']}
onLabel={''}
offLabel={''}
/>
<div>
{__('Elaborazione, in forma elettronica, dei dati relativi ai rapporti e servizi forniti, per lanalisi di comportamenti e preferenze della clientela, da utilizzare a scopo commerciale, per la individuazione ed offerta di prodotti e servizi di suo interesse', 'gepafin')}
</div>
</div>
<div className={classNames(['appForm__row', 'reverseSwitchField'])}>
<FormField
type="switch"
fieldName="thirdparty"
label={''}
control={control}
errors={errors}
defaultValue={values['thirdparty']}
onLabel={''}
offLabel={''}
/>
<div>
{__('Comunicazione dei miei dati ad altre società in ambito bancario, finanziario od assicurativo e enti pubblici che li tratteranno per invio di materiale pubblicitario, vendita diretta, compimento di ricerche di mercato o comunicazione commerciale riguardanti loro prodotti o servizi, mediante le modalità automatizzate e tradizionali di comunicazione sopra indicate', 'gepafin')}
</div>
</div>
<div className="appForm__cols">
<Button
type="button"
severity="info"
onClick={disableAllChecks}
outlined
label={__('Nega tutti', 'gepafin')}/>
<Button
type="button"
severity="info"
onClick={enableAllChecks}
label={__('Attiva tutti', 'gepafin')}/>
</div>
</div>
</Panel>
<Button <Button
label={__('Accedi', 'gepafin')} label={__('Accedi', 'gepafin')}
disabled={loading}/> disabled={loading}/>