Files
GestionIntegralWeb/Frontend/src/pages/Reportes/ReporteTiradasPublicacionesSeccionesPage.tsx

209 lines
8.6 KiB
TypeScript

import React, { useState, useCallback } from 'react';
import {
Box, Typography, Paper, CircularProgress, Alert, Button,
TableContainer, Table, TableHead, TableRow, TableCell, TableBody
} from '@mui/material';
import reportesService from '../../services/Reportes/reportesService';
import type { TiradasPublicacionesSeccionesDto } from '../../models/dtos/Reportes/TiradasPublicacionesSeccionesDto';
import SeleccionaReporteTiradasPublicacionesSecciones from './SeleccionaReporteTiradasPublicacionesSecciones';
import * as XLSX from 'xlsx';
import axios from 'axios';
const ReporteTiradasPublicacionesSeccionesPage: React.FC = () => {
const [reportData, setReportData] = useState<TiradasPublicacionesSeccionesDto[]>([]);
const [loading, setLoading] = useState(false);
const [loadingPdf, setLoadingPdf] = useState(false);
const [error, setError] = useState<string | null>(null);
const [apiErrorParams, setApiErrorParams] = useState<string | null>(null);
const [showParamSelector, setShowParamSelector] = useState(true);
const [currentParams, setCurrentParams] = useState<{
idPublicacion: number;
fechaDesde: string;
fechaHasta: string;
idPlanta?: number | null;
consolidado: boolean;
nombrePublicacion?: string;
nombrePlanta?: string;
mesAnioParaNombreArchivo?: string;
} | null>(null);
const handleGenerarReporte = useCallback(async (params: {
idPublicacion: number;
fechaDesde: string;
fechaHasta: string;
idPlanta?: number | null;
consolidado: boolean;
}) => {
setLoading(true);
setError(null);
setApiErrorParams(null);
setReportData([]);
const pubService = (await import('../../services/Distribucion/publicacionService')).default;
const pubData = await pubService.getPublicacionById(params.idPublicacion);
let plantaNombre = "Consolidado";
if (!params.consolidado && params.idPlanta) {
const plantaService = (await import('../../services/Impresion/plantaService')).default;
const plantaData = await plantaService.getPlantaById(params.idPlanta);
plantaNombre = plantaData?.nombre ?? "N/A";
}
const mesAnioParts = params.fechaDesde.split('-');
const mesAnioNombre = `${mesAnioParts[1]}/${mesAnioParts[0]}`;
setCurrentParams({...params, nombrePublicacion: pubData?.nombre, nombrePlanta: plantaNombre, mesAnioParaNombreArchivo: mesAnioNombre});
try {
const data = await reportesService.getTiradasPublicacionesSecciones(params);
setReportData(data);
if (data.length === 0) {
setError("No se encontraron datos para los parámetros seleccionados.");
}
setShowParamSelector(false);
} catch (err: any) {
const message = axios.isAxiosError(err) && err.response?.data?.message
? err.response.data.message
: 'Ocurrió un error al generar el reporte.';
setApiErrorParams(message);
} finally {
setLoading(false);
}
}, []);
const handleVolverAParametros = useCallback(() => {
setShowParamSelector(true);
setReportData([]);
setError(null);
setApiErrorParams(null);
setCurrentParams(null);
}, []);
const handleExportToExcel = useCallback(() => {
if (reportData.length === 0) {
alert("No hay datos para exportar.");
return;
}
const dataToExport = reportData.map(item => ({
"Nombre Sección": item.nombreSeccion,
"Total Páginas Impresas": item.totalPaginasImpresas,
"Cantidad Ediciones": item.cantidadTiradas,
"Total Páginas x Edición": item.totalPaginasEjemplares,
"Total Ejemplares": item.totalEjemplares,
"Prom. Pág./Ejemplar": item.promedioPaginasPorEjemplar,
}));
const ws = XLSX.utils.json_to_sheet(dataToExport);
const headers = Object.keys(dataToExport[0] || {});
ws['!cols'] = headers.map(h => {
const maxLen = Math.max(...dataToExport.map(row => (row as any)[h]?.toString().length ?? 0), h.length);
return { wch: maxLen + 2 };
});
ws['!freeze'] = { xSplit: 0, ySplit: 1 };
const wb = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(wb, ws, "TiradasSecciones");
let fileName = "ReporteTiradasSecciones";
if (currentParams) {
fileName += `_${currentParams.nombrePublicacion?.replace(/\s+/g, '') ?? `Pub${currentParams.idPublicacion}`}`;
if (!currentParams.consolidado) fileName += `_Planta${currentParams.idPlanta}`;
else fileName += "_Consolidado";
fileName += `_${currentParams.mesAnioParaNombreArchivo?.replace('/', '-')}`;
}
fileName += ".xlsx";
XLSX.writeFile(wb, fileName);
}, [reportData, currentParams]);
const handleGenerarYAbrirPdf = useCallback(async () => {
if (!currentParams) {
setError("Primero debe generar el reporte en pantalla o seleccionar parámetros.");
return;
}
setLoadingPdf(true);
setError(null);
try {
const blob = await reportesService.getTiradasPublicacionesSeccionesPdf(currentParams);
if (blob.type === "application/json") {
const text = await blob.text();
const msg = JSON.parse(text).message ?? "Error inesperado al generar PDF.";
setError(msg);
} else {
const url = URL.createObjectURL(blob);
const w = window.open(url, '_blank');
if (!w) alert("Permite popups para ver el PDF.");
}
} catch {
setError('Ocurrió un error al generar el PDF.');
} finally {
setLoadingPdf(false);
}
}, [currentParams]);
if (showParamSelector) {
return (
<Box sx={{ p: 2, display: 'flex', justifyContent: 'center', mt: 2 }}>
<Paper sx={{ width: '100%', maxWidth: 600 }} elevation={3}>
<SeleccionaReporteTiradasPublicacionesSecciones
onGenerarReporte={handleGenerarReporte}
onCancel={handleVolverAParametros}
isLoading={loading}
apiErrorMessage={apiErrorParams}
/>
</Paper>
</Box>
);
}
return (
<Box sx={{ p: 2 }}>
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', mb: 2, flexWrap: 'wrap', gap: 1 }}>
<Typography variant="h5">Reporte: Tiradas por Publicación y Secciones</Typography>
<Box sx={{ display: 'flex', gap: 1 }}>
<Button onClick={handleGenerarYAbrirPdf} variant="contained" disabled={loadingPdf || reportData.length === 0 || !!error} size="small">
{loadingPdf ? <CircularProgress size={20} color="inherit" /> : "Abrir PDF"}
</Button>
<Button onClick={handleExportToExcel} variant="outlined" disabled={reportData.length === 0 || !!error} size="small">
Exportar a Excel
</Button>
<Button onClick={handleVolverAParametros} variant="outlined" color="secondary" size="small">
Nuevos Parámetros
</Button>
</Box>
</Box>
{loading && <Box sx={{ textAlign: 'center' }}><CircularProgress /></Box>}
{error && !loading && <Alert severity="error" sx={{ my: 2 }}>{error}</Alert>}
{!loading && !error && reportData.length > 0 && (
<TableContainer component={Paper} sx={{ maxHeight: 'calc(100vh - 240px)' }}>
<Table stickyHeader size="small">
<TableHead>
<TableRow>
<TableCell>Nombre Sección</TableCell>
<TableCell align="right">Total Páginas Imp.</TableCell>
<TableCell align="right">Cant. Ediciones</TableCell>
<TableCell align="right">Total Pág. x Edición</TableCell>
<TableCell align="right">Total Ejemplares</TableCell>
<TableCell align="right">Prom. Pág./Ejemplar</TableCell>
</TableRow>
</TableHead>
<TableBody>
{reportData.map((row, idx) => (
<TableRow key={`${row.nombreSeccion}-${idx}`}>
<TableCell>{row.nombreSeccion}</TableCell>
<TableCell align="right">{row.totalPaginasImpresas.toLocaleString('es-AR')}</TableCell>
<TableCell align="right">{row.cantidadTiradas.toLocaleString('es-AR')}</TableCell>
<TableCell align="right">{row.totalPaginasEjemplares.toLocaleString('es-AR')}</TableCell>
<TableCell align="right">{row.totalEjemplares.toLocaleString('es-AR')}</TableCell>
<TableCell align="right">{row.promedioPaginasPorEjemplar.toLocaleString('es-AR')}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
)}
{!loading && !error && reportData.length === 0 && (<Typography>No se encontraron datos para los criterios seleccionados.</Typography>)}
</Box>
);
};
export default ReporteTiradasPublicacionesSeccionesPage;