- added codice ateco as dynamic data;

- stat charts to admin dashboard page;
This commit is contained in:
Vitalii Kiiko
2025-01-13 15:38:23 +01:00
parent 9b14e79c38
commit 10331cf92a
15 changed files with 429 additions and 278 deletions

View File

@@ -9,9 +9,9 @@
"@emailjs/browser": "4.4.1", "@emailjs/browser": "4.4.1",
"@emotion/styled": "11.13.5", "@emotion/styled": "11.13.5",
"@number-flow/react": "0.4.2", "@number-flow/react": "0.4.2",
"@sentry/browser": "^8.42.0", "@sentry/browser": "8.42.0",
"@stomp/stompjs": "^7.0.0", "@stomp/stompjs": "7.0.0",
"@tanstack/react-table": "^8.20.5", "@tanstack/react-table": "8.20.5",
"@wordpress/i18n": "5.13.0", "@wordpress/i18n": "5.13.0",
"@wordpress/react-i18n": "4.13.0", "@wordpress/react-i18n": "4.13.0",
"codice-fiscale-js": "2.3.22", "codice-fiscale-js": "2.3.22",
@@ -37,6 +37,7 @@
"react-hook-form": "7.53.2", "react-hook-form": "7.53.2",
"react-router-dom": "7.0.1", "react-router-dom": "7.0.1",
"react-scripts": "5.0.1", "react-scripts": "5.0.1",
"recharts": "2.15.0",
"sockjs-client": "^1.6.1", "sockjs-client": "^1.6.1",
"validate.js": "0.13.1", "validate.js": "0.13.1",
"zustand": "4.5.4", "zustand": "4.5.4",

View File

@@ -0,0 +1,39 @@
.chartCard {
display: flex;
flex-direction: column;
align-items: center;
gap: 10px;
min-height: 220px;
padding: 20px 5px;
border-radius: 6px;
border: 1px solid #EAB308;
background: #FFF;
}
.chartCard__title {
color: var(--global-textColor);
font-size: 18px;
font-style: normal;
font-weight: 600;
line-height: normal;
margin: 0 0 16px;
}
.chartCard__chart {
width: 100%;
height: 24rem;
}
.chartCard__tooltip {
padding: 5px 10px;
background-color: white;
border: 1px solid var(--global-textColor);
}
.chartCard__tooltipTitle {
font-weight: bold;
}
.chartCard__tooltipText {
}

View File

@@ -50,3 +50,4 @@
@import "./components/evaluation.scss"; @import "./components/evaluation.scss";
@import "./components/fieldsRepeater.scss"; @import "./components/fieldsRepeater.scss";
@import "./components/notificationsSidebar.scss"; @import "./components/notificationsSidebar.scss";
@import "./components/charts.scss";

View File

@@ -0,0 +1,60 @@
import React from 'react';
import { __ } from '@wordpress/i18n';
import { BarChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer } from 'recharts';
import { isEmpty } from 'ramda';
// components
const ChartDomandePerBando = ({ title, data = [] }) => {
const truncateText = (text) => {
const maxLength = 12;
if (typeof text === 'string' && text.length > maxLength) {
return `${text.slice(0, maxLength)}...`;
}
return text;
};
// Custom tooltip
const CustomTooltip = ({ active, payload, label }) => {
if (active && payload && payload.length) {
return (
<div className="chartCard__tooltip">
<p className="chartCard__tooltipTitle">{label}</p>
<p className="chartCard__tooltipText">
{__('Domande', 'gepafin')}: {payload[0].value}
</p>
</div>
);
}
return null;
};
return (<div className="chartCard">
{title ? <span className="chartCard__title">{title}</span> : null}
{data && !isEmpty(data)
? <div className="chartCard__chart">
<ResponsiveContainer width="100%" height="100%">
<BarChart
data={data}
margin={{
top: 20,
right: 30,
left: 20,
bottom: 60,
}}
>
<CartesianGrid strokeDasharray="3 3"/>
<XAxis dataKey="callName" angle={-45} textAnchor="end" height={120}
tickFormatter={truncateText}/>
<YAxis/>
<Tooltip content={<CustomTooltip/>}/>
<Legend/>
<Bar dataKey="numberOfApplications" fill="#EEC137" name={__('Quantità delle domande', 'gepafin')}/>
</BarChart>
</ResponsiveContainer>
</div> : null}
</div>)
}
export default ChartDomandePerBando;

View File

@@ -0,0 +1,58 @@
import React from 'react';
import { __ } from '@wordpress/i18n';
import { Tooltip, ResponsiveContainer, Cell, Pie, PieChart } from 'recharts';
import { isEmpty } from 'ramda';
import getBandoLabel from '../../helpers/getBandoLabel';
// components
const ChartStatoDomande = ({ title, data = [] }) => {
const COLORS = ['#0088FE', '#00C49F', '#FFBB28', '#FF8042', '#8884d8', '#82ca9d'];
const CustomTooltip = ({ active, payload }) => {
if (active && payload && payload.length) {
return (
<div className="chartCard__tooltip">
<p className="chartCard__tooltipTitle">{getBandoLabel(payload[0].name)}</p>
<p className="chartCard__tooltipText">
{__('Domande', 'gepafin')}: {payload[0].value}
</p>
</div>
);
}
return null;
};
return (<div className="chartCard">
{title ? <span className="chartCard__title">{title}</span> : null}
{data && !isEmpty(data)
? <div className="chartCard__chart">
<ResponsiveContainer width="100%" height="100%">
<PieChart>
<Pie
data={data}
cx="50%"
cy="50%"
labelLine={false}
label={({ percent }) => `${(percent * 100).toFixed(0)}%`}
outerRadius={120}
fill="#8884d8"
dataKey="numberOfApplications"
nameKey="status"
>
{data.map((entry, index) => (
<Cell
key={`cell-${index}`}
fill={COLORS[index % COLORS.length]}
/>
))}
</Pie>
<Tooltip content={<CustomTooltip />} />
</PieChart>
</ResponsiveContainer>
</div> : null}
</div>)
}
export default ChartStatoDomande;

View File

@@ -1,50 +0,0 @@
import React, { useEffect, useState } from 'react';
import { Handle, Position } from '@xyflow/react';
import { isEmpty, head } from 'ramda';
// store
import { storeGet, storeSet } from '../../../../store';
import { __ } from '@wordpress/i18n';
const NodeInitialForm = ({ data: { id, label = '' } }) => {
const flowData = storeGet.main.flowData();
const [value, setValue] = useState('');
useEffect(() => {
const flowForms = storeGet.main.flowForms();
const form = head(flowForms.filter(o => String(o.id) === String(id)));
const flowDataItem = head(flowData.filter(o => String(o.formId) === String(id)));
if (form && flowDataItem) {
const field = head(form.content.filter(o => o.id === flowDataItem.chosenField));
if (field) {
const label = head(field.settings.filter(o => o.name === 'label'));
setValue(label ? label.value : field.label);
}
}
}, [flowData]);
return (
<div className="nodeInitialForm">
<label>
{label}
</label>
<span>{value}</span>
{/*{options && !isEmpty(options)
? <select onChange={onChangeFn} value={value}>
<option value="">{__('Scegli il campo', 'gepafin')}</option>
{options.map(o => <option key={o.name} value={o.name}>
{o.label}
</option>)}
</select> : null}*/}
<Handle
type="source"
position={Position.Bottom}
isConnectable={true}
/>
</div>
);
}
export default NodeInitialForm;

View File

@@ -1,78 +0,0 @@
import React, { useEffect, useState } from 'react';
import { Handle, Position } from '@xyflow/react';
import { head, isEmpty } from 'ramda';
import { __ } from '@wordpress/i18n';
import { useStore, storeSet, storeGet } from '../../../../store';
const NodeIntermediateForm = ({ data: { id, label = '' } }) => {
const flowEdges = useStore().main.flowEdges();
const flowData = useStore().main.flowData();
const [options, setOptions] = useState([]);
const [value, setValue] = useState('');
const onChangeFn = (e) => {
const { value } = e.target;
const data = {
formId: String(id),
chosenField: '',
chosenValue: value
}
setValue(value);
storeSet.main.addFlowData(data);
}
useEffect(() => {
const edge = head(flowEdges.filter(o => o.target === String(id)));
if (edge) {
const sourceForm = edge.source;
const sourceFormData = head(flowData.filter(o => String(o.formId) === sourceForm));
const flowForms = storeGet.main.flowForms();
const form = head(flowForms.filter(o => String(o.id) === String(sourceForm)));
if (form && sourceFormData) {
const { chosenField } = sourceFormData;
const field = head(form.content.filter(o => o.id === chosenField));
if (field) {
const options = head(field.settings.filter(o => o.name === 'options'));
if (options) {
setOptions(options.value);
}
}
}
}
const flowDataForm = head(flowData.filter(o => String(o.formId) === String(id)));
if (flowDataForm) {
setValue(flowDataForm.chosenValue);
}
}, [flowEdges, flowData]);
return (
<div className="nodeIntermediateForm">
<Handle
type="target"
position={Position.Top}
isConnectable={true}
/>
<label>
{label}
</label>
{options && !isEmpty(options)
? <select onChange={onChangeFn} value={value}>
<option value="">{__('Scegli il valore', 'gepafin')}</option>
{options.map(o => <option key={o.name} value={o.name}>
{o.label}
</option>)}
</select> : null}
<Handle
type="source"
position={Position.Bottom}
isConnectable={true}
/>
</div>
);
}
export default NodeIntermediateForm;

View File

@@ -1,132 +0,0 @@
import React, { useEffect, useState } from 'react';
import {
ReactFlow,
Background
} from '@xyflow/react';
import { isEmpty } from 'ramda';
import '@xyflow/react/dist/style.css';
// store
import { useStore, storeSet } from '../../store';
// nodes
import NodeInitialForm from './components/NodeInitialForm';
import NodeIntermediateForm from './components/NodeIntermediateForm';
const nodeTypes = {
initialForm: NodeInitialForm,
intermediateForm: NodeIntermediateForm
};
const FlowBuilder = ({ initialForm = 0, finalForm = 0, mainField = '' }) => {
const flowForms = useStore().main.flowForms();
const [nodes, setNodes] = useState([]);
const [edges, setEdges] = useState([]);
const range = (start, stop, step) => {
return Array.from(
{ length: (stop - start) / step + 1 },
(_, i) => start + i * step
);
}
useEffect(() => {
if (
(flowForms.length === 2 && initialForm) ||
(flowForms.length > 2 && initialForm && finalForm)
) {
const total = (flowForms.length - 2) * (200 - 90);
let coordinates = range(total * -1, total, 200);
const initialNodes = flowForms.map(o => {
const formId = String(o.id);
let obj;
if (formId === String(initialForm)) {
obj = {
id: formId,
type: 'initialForm',
data: { label: o.label, id: formId },
position: { x: 0, y: 0 },
}
} else if (formId === String(finalForm)) {
obj = {
id: formId,
type: 'output',
data: { label: o.label, id: formId },
position: { x: 0, y: flowForms.length === 2 ? 150 : 300 },
}
} else {
const x = coordinates.splice(0, 1);
obj = {
id: formId,
type: 'intermediateForm',
data: { label: o.label, id: formId },
position: { x, y: 150 },
}
}
return obj
});
let edges = [];
// eslint-disable-next-line
flowForms.map(o => {
const formId = String(o.id);
if (formId !== String(initialForm) && formId !== String(finalForm)) {
edges.push({
id: `${initialForm}->${formId}`,
source: String(initialForm),
target: formId,
type: 'smoothstep'
});
}
if (formId !== String(initialForm) && formId !== String(finalForm) && String(finalForm) !== '0') {
edges.push({
id: `${formId}->${finalForm}`,
source: formId,
target: String(finalForm),
type: 'smoothstep'
});
}
});
if (flowForms.length === 2 && initialForm && finalForm) {
edges.push({
id: `${initialForm}->${finalForm}`,
source: String(initialForm),
target: String(finalForm),
type: 'smoothstep'
});
}
setNodes(initialNodes);
setEdges(edges);
storeSet.main.flowEdges(edges);
} else {
setNodes([]);
setEdges([]);
}
}, [initialForm, finalForm, flowForms, mainField]);
return (
!isEmpty(nodes) && !isEmpty(edges)
? <div className="flowBuilder__wrapper">
<ReactFlow
nodes={nodes}
edges={edges}
nodesDraggable={false}
nodesConnectable={false}
fitView
nodeTypes={nodeTypes}
attributionPosition="top-right"
>
<Background variant="dots" gap={12} size={1}/>
</ReactFlow>
</div>
: null
);
}
export default FlowBuilder;

View File

@@ -19,6 +19,7 @@ const dynamicDataForTextinput = [
{ label: 'ragione sociale', value: 'company.companyName' }, { label: 'ragione sociale', value: 'company.companyName' },
{ label: 'partita IVA', value: 'company.vatNumber' }, { label: 'partita IVA', value: 'company.vatNumber' },
{ label: 'codice fiscale azienda', value: 'company.codiceFiscale' }, { label: 'codice fiscale azienda', value: 'company.codiceFiscale' },
{ label: 'codice ateco', value: 'company.codiceAteco' },
{ label: 'indirizzo', value: 'company.address' }, { label: 'indirizzo', value: 'company.address' },
{ label: 'numero di telefono azienda', value: 'company.phoneNumber' }, { label: 'numero di telefono azienda', value: 'company.phoneNumber' },
{ label: 'città', value: 'company.city' }, { label: 'città', value: 'company.city' },

View File

@@ -64,11 +64,25 @@ const AppSidebar = () => {
}, },
{ {
label: __('Archivio domande', 'gepafin'), label: __('Archivio domande', 'gepafin'),
icon: 'pi pi-file', icon: 'pi pi-briefcase',
href: '/domande', href: '/domande',
id: 7, id: 7,
enable: intersection(permissions, ['APPLY_CALLS']).length enable: intersection(permissions, ['APPLY_CALLS']).length
}, },
{
label: __('Archivio domande', 'gepafin'),
icon: 'pi pi-briefcase',
href: '/domande-archivio',
id: 5,
enable: intersection(permissions, ['VIEW_USERS', 'MANAGE_USERS']).length
},
{
label: __('Archivio domande', 'gepafin'),
icon: 'pi pi-briefcase',
href: '/domande-archivio',
id: 6,
enable: intersection(permissions, ['EVALUATE_APPLICATIONS']).length
},
{ {
label: __('Soccorso istruttorio', 'gepafin'), label: __('Soccorso istruttorio', 'gepafin'),
icon: <HelpIcon/>, icon: <HelpIcon/>,

View File

@@ -312,7 +312,8 @@ const BandoApplication = () => {
dynamicData = Object.keys(company).reduce((acc, cur) => { dynamicData = Object.keys(company).reduce((acc, cur) => {
if ([ if ([
'companyName', 'vatNumber', 'codiceFiscale', 'address', 'phoneNumber', 'companyName', 'vatNumber', 'codiceFiscale', 'address', 'phoneNumber',
'city', 'province', 'cap', 'country', 'pec', 'email', 'contactName', 'contactEmail' 'city', 'province', 'cap', 'country', 'pec', 'email', 'contactName', 'contactEmail',
'codiceAteco'
].includes(cur)) { ].includes(cur)) {
acc.company[cur] = company[cur]; acc.company[cur] = company[cur];
} }

View File

@@ -4,33 +4,31 @@ import { useNavigate } from 'react-router-dom';
import { pathOr } from 'ramda'; import { pathOr } from 'ramda';
import NumberFlow from '@number-flow/react'; import NumberFlow from '@number-flow/react';
// store
//import { storeSet } from '../../store';
// api // api
import DashboardService from '../../service/dashboard-service'; import DashboardService from '../../service/dashboard-service';
// components // components
import LatestBandiTable from './components/LatestBandiTable'; import LatestBandiTable from './components/LatestBandiTable';
//import LatestUsersActivityTable from './components/LatestUsersActivityTable';
import { Button } from 'primereact/button'; import { Button } from 'primereact/button';
//import MyEvaluationsTable from '../DashboardPreInstructor/components/PreInstructorDomandeTable';
import AllDomandeTable from '../Domande/components/AllDomandeTable'; import AllDomandeTable from '../Domande/components/AllDomandeTable';
import DraftApplicationsTable from './components/DraftApplicationsTable'; import DraftApplicationsTable from './components/DraftApplicationsTable';
import ChartDomandePerBando from '../../components/ChartDomandePerBando';
import ChartStatoDomande from '../../components/ChartStatoDomande';
const Dashboard = () => { const Dashboard = () => {
const navigate = useNavigate(); const navigate = useNavigate();
const [mainStats, setMainStats] = useState({}); const [mainStats, setMainStats] = useState({});
const [chartStats, setChartStats] = useState({});
const onGoToCreateNewBando = () => { const onGoToCreateNewBando = () => {
navigate('/bandi/new'); navigate('/bandi/new');
} }
/*const onGoToUsers = () => { const onGoToUsers = () => {
console.log('onGoToUsers') navigate('/utenti');
} }
const onGoToStats = () => { /*const onGoToStats = () => {
console.log('onGoToStats') console.log('onGoToStats')
} }
@@ -45,10 +43,12 @@ const Dashboard = () => {
const getStats = (data) => { const getStats = (data) => {
if (data.status === 'SUCCESS') { if (data.status === 'SUCCESS') {
setMainStats(data.data.widget1); setMainStats(data.data.widget1);
setChartStats(data.data.widgetBars);
} }
} }
const errGetStats = () => {} const errGetStats = () => {
}
useEffect(() => { useEffect(() => {
DashboardService.getAdminStats(getStats, errGetStats); DashboardService.getAdminStats(getStats, errGetStats);
@@ -147,20 +147,36 @@ const Dashboard = () => {
<div className="appPage__spacer"></div> <div className="appPage__spacer"></div>
{chartStats.applicationPerCall
? <div className="appPageSection">
<h2>{__('Statistiche di sistema', 'gepafin')}</h2>
<div className="appPageSection columns">
<ChartDomandePerBando
title={__('Domande per bando', 'gepafin')}
data={chartStats.applicationPerCall}/>
<ChartStatoDomande
title={__('Stato domande', 'gepafin')}
data={chartStats.applicationPerStatus}/>
</div>
</div> : null}
<div className="appPage__spacer"></div>
<div className="appPageSection__hr"> <div className="appPageSection__hr">
<span>{__('Azioni rapide', 'gepafin')}</span> <span>{__('Azioni rapide', 'gepafin')}</span>
</div> </div>
<div className="appPage__spacer"></div>
<div className="appPageSection"> <div className="appPageSection">
<div className="appPageSection__actions"> <div className="appPageSection__actions">
<Button <Button
onClick={onGoToCreateNewBando} onClick={onGoToCreateNewBando}
label={__('Crea nuovo bando', 'gepafin')} icon="pi pi-plus" iconPos="right"/> label={__('Crea nuovo bando', 'gepafin')} icon="pi pi-plus" iconPos="right"/>
{/*<Button <Button
disabled={true}
onClick={onGoToUsers} onClick={onGoToUsers}
label={__('Gestione utenti', 'gepafin')} icon="pi pi-users" iconPos="right"/> label={__('Gestione utenti', 'gepafin')} icon="pi pi-users" iconPos="right"/>
<Button {/*<Button
disabled={true} disabled={true}
onClick={onGoToStats} onClick={onGoToStats}
label={__('Report mensile', 'gepafin')} icon="pi pi-chart-bar" iconPos="right"/> label={__('Report mensile', 'gepafin')} icon="pi pi-chart-bar" iconPos="right"/>

View File

@@ -0,0 +1,182 @@
import React, { useState, useEffect } from 'react';
import { __ } from '@wordpress/i18n';
import { is, uniq } from 'ramda';
import { Link, useLocation } from 'react-router-dom';
// api
import ApplicationService from '../../../../service/application-service';
// tools
import getBandoLabel from '../../../../helpers/getBandoLabel';
import getBandoSeverity from '../../../../helpers/getBandoSeverity';
// translation
import translationStrings from '../../../../translationStringsForComponents';
// 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';
const AllDomandeArchiveTable = ({ updaterString = '' }) => {
const [items, setItems] = useState(null);
const [filters, setFilters] = useState(null);
const [localAsyncRequest, setLocalAsyncRequest] = useState(false);
const [statuses, setStatuses] = useState([]);
const location = useLocation();
useEffect(() => {
setLocalAsyncRequest(true);
ApplicationService.getApplications(getCallback, errGetCallbacks, [
['statuses', ['APPROVED', 'REJECTED']]
]);
}, [updaterString]);
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.callEndDate = is(String, d.callEndDate) ? new Date(d.callEndDate) : (d.callEndDate ? d.callEndDate : '');
d.modifiedDate = is(String, d.modifiedDate) ? new Date(d.modifiedDate) : (d.modifiedDate ? d.modifiedDate : '');
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 },
callTitle: {
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 }]
},
callEndDate: {
operator: FilterOperator.AND,
constraints: [{ value: null, matchMode: FilterMatchMode.DATE_IS }]
},
status: { operator: FilterOperator.OR, constraints: [{ value: null, matchMode: FilterMatchMode.EQUALS }] },
});
};
const renderHeader = () => {
return (
<div className="appTableHeader">
<Button type="button" icon="pi pi-filter-slash" label={__('Pulisci', 'gepafin')} outlined
onClick={clearFilter}/>
</div>
);
};
const dateAppliedBodyTemplate = (rowData) => {
return formatDate(rowData.submissionDate);
};
const statusFilterTemplate = (options) => {
return <Dropdown value={options.value} options={statuses}
onChange={(e) => options.filterCallback(e.value, options.index)}
itemTemplate={statusItemTemplate} placeholder={translationStrings.selectOneLabel} className="p-column-filter"
showClear/>;
};
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"/>;
};
const statusBodyTemplate = (rowData) => {
return <ProperBandoLabel status={rowData.status}/>;
};
const statusItemTemplate = (option) => {
return <Tag value={getBandoLabel(option)} severity={getBandoSeverity(option)}/>;
};
const actionsBodyTemplate = (rowData) => {
return <div className="appPageSection__tableActions lessGap">
<Link to={`/domande-archivio/${rowData.id}/preview`}>
<Button severity="info" label={__('Anteprima', 'gepafin')} icon="pi pi-eye" size="small"
iconPos="right"/>
</Link>
</div>
}
const header = renderHeader();
return (
<div className="appPageSection__table">
<DataTable value={items} paginator showGridlines rows={10} loading={localAsyncRequest} dataKey="id"
filters={filters} stripedRows removableSort
header={header}
emptyMessage={translationStrings.emptyMessage}
onFilter={(e) => setFilters(e.filters)}>
<Column field="id" header={__('ID domanda', 'gepafin')}
sortable filterPlaceholder={__('Cerca', 'gepafin')}
style={{ minWidth: '6rem' }}/>
<Column field="protocolNumber" header={__('Protocollo', 'gepafin')}
sortable filterPlaceholder={__('Cerca', 'gepafin')}
style={{ minWidth: '6rem' }}/>
<Column field="callTitle" header={__('Bando', 'gepafin')}
filter sortable
filterPlaceholder={__('Cerca', 'gepafin')}
style={{ minWidth: '10rem' }}/>
<Column field="companyName" header={__('Azienda Beneficiaria', 'gepafin')}
filter sortable
filterPlaceholder={__('Cerca il nome', 'gepafin')}
style={{ minWidth: '8rem' }}/>
<Column header={__('Data Ricezione', 'gepafin')}
filterField="submissionDate" dataType="date"
style={{ minWidth: '8rem' }}
body={dateAppliedBodyTemplate} filter filterElement={dateFilterTemplate}/>
<Column field="assignedUserName" header={__('Assegnato', 'gepafin')}
filter sortable
filterPlaceholder={__('Cerca il nome', 'gepafin')}
style={{ minWidth: '8rem' }}/>
<Column field="status" header={__('Stato', 'gepafin')}
style={{ minWidth: '8rem' }} body={statusBodyTemplate}
filter
filterElement={statusFilterTemplate}/>
<Column header={__('Azioni', 'gepafin')}
body={actionsBodyTemplate}/>
</DataTable>
</div>
)
}
export default AllDomandeArchiveTable;

View File

@@ -0,0 +1,25 @@
import React from 'react';
import { __ } from '@wordpress/i18n';
// components
import AllDomandeArchiveTable from './components/AllDomandeArchiveTable';
const Domande = () => {
return (
<div className="appPage">
<div className="appPage__pageHeader">
<h1>{__('Archivio domande', 'gepafin')}</h1>
</div>
<div className="appPage__spacer"></div>
<div className="appPageSection">
<h2>{__('Domande pubblicate', 'gepafin')}</h2>
<AllDomandeArchiveTable/>
</div>
</div>
)
}
export default Domande;

View File

@@ -40,6 +40,7 @@ import BandiPreferredBeneficiario from './pages/BandiPreferredBeneficiario';
import DomandeInstructorManager from './pages/DomandeInstructorManager'; import DomandeInstructorManager from './pages/DomandeInstructorManager';
import DomandaEditInstructorManager from './pages/DomandaEditInstructorManager'; import DomandaEditInstructorManager from './pages/DomandaEditInstructorManager';
import UserActivity from './pages/UserActivity'; import UserActivity from './pages/UserActivity';
import DomandeArchive from './pages/DomandeArchive';
const routes = ({ role, chosenCompanyId }) => { const routes = ({ role, chosenCompanyId }) => {
@@ -124,6 +125,18 @@ const routes = ({ role, chosenCompanyId }) => {
{'ROLE_PRE_INSTRUCTOR' === role ? <BandoApplicationPreview/> : null} {'ROLE_PRE_INSTRUCTOR' === role ? <BandoApplicationPreview/> : null}
{'ROLE_INSTRUCTOR_MANAGER' === role ? <BandoApplicationPreview/> : null} {'ROLE_INSTRUCTOR_MANAGER' === role ? <BandoApplicationPreview/> : null}
</DefaultLayout>}/> </DefaultLayout>}/>
<Route path="/domande-archivio" element={<DefaultLayout>
{'ROLE_SUPER_ADMIN' === role ? <DomandeArchive/> : null}
{'ROLE_BENEFICIARY' === role ? <PageNotFound/> : null}
{'ROLE_PRE_INSTRUCTOR' === role ? <DomandeArchive/> : null}
{'ROLE_INSTRUCTOR_MANAGER' === role ? <DomandeArchive/> : null}
</DefaultLayout>}/>
<Route path="/domande-archivio/:id/preview" element={<DefaultLayout>
{'ROLE_SUPER_ADMIN' === role ? <BandoApplicationPreview/> : null}
{'ROLE_BENEFICIARY' === role ? <PageNotFound/> : null}
{'ROLE_PRE_INSTRUCTOR' === role ? <BandoApplicationPreview/> : null}
{'ROLE_INSTRUCTOR_MANAGER' === role ? <BandoApplicationPreview/> : null}
</DefaultLayout>}/>
<Route path="/domande/:id/aggiungi-soccorso" element={<DefaultLayout> <Route path="/domande/:id/aggiungi-soccorso" element={<DefaultLayout>
{'ROLE_SUPER_ADMIN' === role ? <PageNotFound/> : null} {'ROLE_SUPER_ADMIN' === role ? <PageNotFound/> : null}
{'ROLE_BENEFICIARY' === role ? <PageNotFound/> : null} {'ROLE_BENEFICIARY' === role ? <PageNotFound/> : null}