import { TableContainer, Paper, Table, TableHead, TableRow, TableCell, TableBody, CircularProgress, Typography, IconButton, Tooltip, Box, Menu, MenuItem, } from "@mui/material"; import CheckIcon from "@mui/icons-material/Check"; import CloseIcon from "@mui/icons-material/Close"; import ScheduleIcon from "@mui/icons-material/Schedule"; import Visibility from "@mui/icons-material/Visibility"; import VisibilityOff from "@mui/icons-material/VisibilityOff"; import { ImportProcess } from "@/types"; import { useGetImportProcesses } from "@/hooks/useGetImportProcesses"; import { ErrorScreen } from "./ErrorScreen"; import { LoadingScreen } from "./LoadingScreen"; import { ButtonTooltip } from "./ButtonTooltip"; import { useState, useEffect } from "react"; import { getFileNameFromURL } from "@/utils"; export interface ImportProcessTableProps { dataServiceEid?: number; recipeEid?: number; projectEid?: number; showProjectColumn?: boolean; } const REFRESH_INTERVAL = 5000; export function ImportProcessTable(props: ImportProcessTableProps) { const { data: importProcessList, error, loading, refresh, } = useGetImportProcesses(props); const showProjectColumn = props.showProjectColumn !== false; useEffect(() => { const interval = setInterval(refresh, REFRESH_INTERVAL); return () => clearInterval(interval); // eslint-disable-next-line react-hooks/exhaustive-deps }, []); if (error) { return <ErrorScreen message={error} />; } if (loading && !importProcessList) { return <LoadingScreen />; } if (!importProcessList) { return <ErrorScreen />; } if (importProcessList.length === 0) { return ( <Typography variant="subtitle1" color={"GrayText"}> Aucun import process </Typography> ); } return ( <TableContainer component={Paper}> <Table sx={{ minWidth: 650 }} aria-label="simple table"> <TableHead> <TableRow> <TableCell>eid</TableCell> <TableCell>Date</TableCell> <TableCell>Recette</TableCell> <TableCell>État</TableCell> {showProjectColumn ? <TableCell>Projet</TableCell> : null} <TableCell width={50}>Dataset</TableCell> <TableCell width={50}>Log</TableCell> <TableCell width={50}>SHACL</TableCell> </TableRow> </TableHead> <TableBody> {importProcessList.map((row, i) => ( <ImportProcessTableRow key={i} importProcess={row} showProjectColumn={showProjectColumn} /> ))} </TableBody> </Table> </TableContainer> ); } function ImportProcessTableRow({ importProcess, showProjectColumn, }: { importProcess: ImportProcess; showProjectColumn: boolean; }) { const hasLog = importProcess.log_url != null; const dateObj = new Date(importProcess.date); return ( <TableRow key={importProcess.eid}> <TableCell component="th" scope="row"> {importProcess.eid} </TableCell> <TableCell>{dateObj.toLocaleString()}</TableCell> <TableCell>{importProcess.recipe}</TableCell> <TableCell> <StateIcon state={importProcess.state} /> </TableCell> {showProjectColumn ? ( <TableCell>{importProcess.project}</TableCell> ) : null} <DatasetButton importProcess={importProcess} /> <TableCell> <ButtonTooltip title={hasLog ? "Voir le fichier de log" : "Aucun log disponible"} > <IconButton color="primary" disabled={!hasLog} href={importProcess.log_url ?? ""} download={getFileNameFromURL(importProcess.log_url)} > {hasLog ? <Visibility /> : <VisibilityOff />} </IconButton> </ButtonTooltip> </TableCell> <ShaclButton importProcess={importProcess} /> </TableRow> ); } function DatasetButton({ importProcess }: { importProcess: ImportProcess }) { const hasDataset = importProcess.input_dataset_url != null || importProcess.output_dataset_url != null; const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null); const open = Boolean(anchorEl); const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => { setAnchorEl(event.currentTarget); }; const handleClose = () => { setAnchorEl(null); }; return ( <TableCell> <ButtonTooltip title={ hasDataset ? "Voir le fichier de Dataset" : "Aucun Dataset disponible" } > <IconButton color="primary" disabled={!hasDataset} onClick={handleClick} > {hasDataset ? <Visibility /> : <VisibilityOff />} </IconButton> </ButtonTooltip> <Menu id="dataset-menu" anchorEl={anchorEl} open={open} onClose={handleClose} MenuListProps={{ "aria-labelledby": "dataset-menu", }} > <DatasetMenuItem text="Entrée" url={importProcess.input_dataset_url} onClick={handleClose} /> <DatasetMenuItem text="Sortie" url={importProcess.output_dataset_url} onClick={handleClose} /> </Menu> </TableCell> ); } function DatasetMenuItem({ text, url, onClick, }: { text: string; url?: string; onClick: () => void; }) { if (!url) { return ( <MenuItem onClick={onClick} disabled={true}> {text} </MenuItem> ); } // Putting href and download props directly on the MenuItem does not work return ( <a href={url} download={getFileNameFromURL(url)} style={{ color: "inherit", textDecoration: "inherit", }} > <MenuItem onClick={onClick} disabled={false}> {text} </MenuItem> </a> ); } function ShaclButton({ importProcess }: { importProcess: ImportProcess }) { if (importProcess.shacl_valid) { return ( <TableCell> <Tooltip title="La validation SHACL a réussi"> <Box style={{ display: "flex", justifyContent: "center", alignItems: "center", }} > <CheckIcon /> </Box> </Tooltip> </TableCell> ); } return ( <TableCell> <ButtonTooltip title={"Voir le rapport d'erreur SHACL"}> <IconButton color="primary" href={importProcess.shacl_report_url ?? ""} download={getFileNameFromURL(importProcess.shacl_report_url)} > <Visibility color="warning" /> </IconButton> </ButtonTooltip> </TableCell> ); } function StateIcon({ state }: { state: ImportProcess["state"] }) { if (state === "successful") { return <CheckIcon />; } if (state === "error") { return <CloseIcon color="error" />; } if (state === "waiting") { return <ScheduleIcon color="warning" />; } if (state === "ongoing") { return <CircularProgress size={20} />; } }