Skip to content

Commit

Permalink
Merge pull request #67 from consiglionazionaledellericerche/42-implem…
Browse files Browse the repository at this point in the history
…entare-pagina-buoni-pasto

implementata pagina e componenti per la visualizzazione della situazione buoni pasto
  • Loading branch information
criluc authored Jan 27, 2025
2 parents 7f173b2 + 0f8306b commit 88df5e0
Show file tree
Hide file tree
Showing 14 changed files with 982 additions and 2 deletions.
4 changes: 2 additions & 2 deletions components/layout/menu/personalDataMenu.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { faCalendarDays, faMoneyBill, faUserXmark, faPlane, faCalendar, faClock,faGraduationCap } from "@fortawesome/free-solid-svg-icons"
import { faCalendarDays, faMoneyBill, faUserXmark, faPlane, faCalendar, faClock,faGraduationCap, faUtensils } from "@fortawesome/free-solid-svg-icons"
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"
import { NavDropdown } from "react-bootstrap"
import { useSession} from "next-auth/react"
Expand Down Expand Up @@ -37,8 +37,8 @@ const PersonalDataMenu: React.FC<PersonalDataMenuProps> = ({ personInfo }) => {
}
<NavDropdown.Item href="/trainingRecap"><FontAwesomeIcon icon={faGraduationCap} /> Ore di formazione</NavDropdown.Item>
<NavDropdown.Item href="/personMonthsHoursRecap"><FontAwesomeIcon icon={faClock} /> Riepilogo orario</NavDropdown.Item>
<NavDropdown.Item href="/mealTicketsRecap"><FontAwesomeIcon icon={faUtensils} /> Riepilogo buoni pasto</NavDropdown.Item>
{/*
<NavDropdown.Item href="#action/3.5">Competenze</NavDropdown.Item>
<NavDropdown.Item href="#action/3.8">Ruoli in ePAS all`interno della sede</NavDropdown.Item>
<NavDropdown.Divider />
Expand Down
129 changes: 129 additions & 0 deletions components/mealTickets/mealTicketsTable.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
import React, { useEffect, useState } from 'react';
import { Table, Button } from "react-bootstrap";
import DataTable from 'react-data-table-component';
import { BlockMealTicket } from "../../types/blockMealTicket";

interface MealTicketsTableProps {
data: BlockMealTicket[] ;
}

const MealTicketsTable: React.FC<MealTicketsTableProps> = ({
data
}) => {
// Stato per i dati e il caricamento
const [loading, setLoading] = useState<boolean>(true);
const [error, setError] = useState<string | null>(null);
const [searchTerm, setSearchTerm] = useState('');
const [rowsPerPage, setRowsPerPage] = useState(10);
const [currentPage, setCurrentPage] = useState(1);
const [totalRows, setTotalRows] = useState(0);

const columns = [
{
name: <strong>Codice</strong>,
selector: (row: any) => (
<>
<strong>{row.codeBlock}</strong> ({row.first}-{row.last})
</>
),
sortable: true,
},
{
name: <strong>Consegnato il</strong>,
selector: (row: any) => row.getReceivedDate,
sortable: true,
},
{
name: <strong>Scadenza</strong>,
selector: (row: any) => row.getExpireDate,
sortable: true,
},
{
name: <strong>Maturati</strong>,
selector: (row: any) => (
<>
<h5 className="text-danger"><strong>{row.getConsumed}</strong></h5>
</>
),
sortable: true,
},
{
name: <strong>Da Maturare</strong>,
selector: (row: any) => (
<>
<h5 className="text-success"><strong>{row.getRemaining}</strong></h5>
</>
),
sortable: true,
},
];

const filteredData = data.filter(item =>
item.codeBlock?.toLowerCase().includes(searchTerm.toLowerCase())
);

const paginatedData = filteredData.slice((currentPage - 1) * rowsPerPage, currentPage * rowsPerPage);

const handlePageChange = (page: number) => {
setCurrentPage(page);
};

const handleRowsPerPageChange = (newRowsPerPage: number) => {
setRowsPerPage(newRowsPerPage);
setCurrentPage(1);
};

const paginationComponentOptions = {
rowsPerPageText: 'Elementi per pagina',
rangeSeparatorText: 'di',
selectAllRowsItem: true,
selectAllRowsItemText: 'Tutti',
};

return (
<div>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '10px' }}>
<div>
<span>Visualizza</span>
<select
value={rowsPerPage}
onChange={(e) => handleRowsPerPageChange(Number(e.target.value))}
style={{ marginLeft: '5px' }}
>
<option value={10}>10</option>
<option value={20}>20</option>
<option value={30}>30</option>
</select>
<span style={{ marginLeft: '5px' }}>elementi</span>
</div>
<input
type="text"
placeholder="Cerca"
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
style={{ marginRight: '5px' }}
/>
</div>
<DataTable
className="table datatable-mealTicket table-condensed center"
columns={columns}
data={paginatedData}
pagination
paginationServer
paginationTotalRows={filteredData.length}
paginationPerPage={rowsPerPage}
paginationDefaultPage={currentPage}
onChangePage={handlePageChange}
paginationComponentOptions={paginationComponentOptions}
highlightOnHover
/>
<div style={{ marginTop: '20px' }}>
Vista da {(currentPage - 1) * rowsPerPage + 1} a {Math.min(currentPage * rowsPerPage, filteredData.length)} di {filteredData.length} elementi (filtrati da {totalRows} elementi totali)
</div>
</div>
);
}

export default MealTicketsTable;


64 changes: 64 additions & 0 deletions components/mealTickets/mealTicketsView.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { Col, Container, Row } from "react-bootstrap";
import MealTicketsTable from "./mealTicketsTable";
import { MealTicketRecapShow } from "../../types/mealTicketRecapShow";
import DateUtility from "../../utils/dateUtility";
import { faLightbulb } from "@fortawesome/free-solid-svg-icons"
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"

interface MealTicketsViewProps {
mealTicketsData: MealTicketRecapShow
}

const MealTicketsView: React.FC<MealTicketsViewProps> = ({
mealTicketsData
}) => {

let fullname = <span>{mealTicketsData.person?.surname} {mealTicketsData.person?.name}</span>;
let recap = mealTicketsData.recap;
let messageDateRunOut = recap?.dateRunOut ? (
<>
<div className="alert alert-danger">
<p>Tutti i buoni pasto consegnati sono stati maturati in data {DateUtility.formatDateLocal(recap?.dateRunOut)}.</p>
<p>Sono stati maturati <strong>{recap?.remaining}</strong> buoni pasto oltre quelli consegnati.</p>
<p>Contattare l&apos;amministrazione per ricevere i nuovi blocchetti.</p></div>
</>) :
(<>
<div className="alert alert-success">
<p>Rimangono <strong>{recap?.remaining}</strong> buoni pasto consegnati ancora da maturare.</p>
</div>
</>);
let messageInfo = <>
<div className="alert alert-info">
<p>Elenco dei blocchi consegnati a <strong>{fullname}</strong>.</p>
<p>I valori <strong>Maturati / Da Maturare</strong> sono puramente indicativi e sono ottenuti calcolando i buoni pasto maturati
partendo da quelli con scadenza più imminente e dal codice progressivo minore.</p>

<p><FontAwesomeIcon icon={faLightbulb} /> Cliccando sui titoli delle colonne è possibile ordinare
i blocchi consegnati per <em>Codice Blocco</em>, <em>Data Consegna</em>, <em>Data Scadenza</em> e <em>Maturati/Da Maturare</em>
</p>
</div>
</>
return (
<>
<Container fluid>
<Row>
<Col sm={1} />
<Col sm={9}>
<div className="page-header">
<h2>Riepilogo Buoni Pasto {fullname}</h2>
<br/>
</div>
{messageDateRunOut}
{messageInfo}
{recap?.blockMealTicketReceivedDeliveryDesc && (
<MealTicketsTable data={recap.blockMealTicketReceivedDeliveryDesc} />
)}
</Col>
<Col sm={2} />
</Row>
</Container>
</>
);
}

export default MealTicketsView
81 changes: 81 additions & 0 deletions components/mealTickets/modal/trainingHoursModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import React, { useState, useEffect } from "react";
import Modal from 'react-bootstrap/Modal';
import Button from 'react-bootstrap/Button';
import TrainingHoursModalContent from './trainingHoursModalContent';
import { secureCheck } from '../../../utils/secureCheck';

interface TrainingHoursModalProps {
tmpshow: boolean;
close: Function;
parameters: any;
showError: (message: string) => void;
showSuccess: (message: string) => void;
}

const TrainingHoursModal: React.FC<TrainingHoursModalProps> = ({
tmpshow,
close,
parameters,
showError,
showSuccess
}) => {
const [show, setShow] = useState(false);
const [dataTraining, setDataTraining] = useState<any>(null);
const [titleModal, setTitle] = useState("");

useEffect(() => {
let title = "";
if (parameters.action === 'insert' || parameters.action === 'edit') {
title = `Ore formazione ${parameters.month.name} ${parameters.year}`;
} else {
title = `Elimina ore di formazione`;
}
setTitle(title);
}, [parameters.action]);


useEffect(() => {
if (tmpshow) {
// Verifica della sicurezza e caricamento dati
async function getData() {
setShow(true);
}
getData();
} else {
setShow(false);
}
}, [tmpshow, parameters]);

const handleClose = () => {
setShow(false);
close();
};

return (
<>
<Modal
show={show}
onHide={handleClose}
size="xl"
aria-labelledby="modal-absence-info"
>
<Modal.Header closeButton>
<Modal.Title>{titleModal}</Modal.Title>
</Modal.Header>
<Modal.Body>
{show && <TrainingHoursModalContent
year={parameters.year}
month={parameters.month}
action={parameters.action}
pm={parameters}
handleClose={handleClose}
showError={showError}
showSuccess={showSuccess}
/>}
</Modal.Body>
</Modal>
</>
);
}

export default TrainingHoursModal;
Loading

0 comments on commit 88df5e0

Please sign in to comment.