github.com/pelicanplatform/pelican@v1.0.5/web_ui/frontend/components/DataExportTable.tsx (about) 1 import {Table, TableCell, TableBody, TableContainer, TableHead, TableRow, Paper, Typography, Box} from '@mui/material'; 2 import React, {FunctionComponent, ReactElement, useEffect, useMemo, useRef, useState} from "react"; 3 import {Skeleton} from "@mui/material"; 4 5 6 7 interface Record { 8 [key: string]: string | number | boolean | null 9 } 10 11 interface ExportData extends Record { 12 "Type": string 13 "Local Path": string 14 "Namespace Prefix": string 15 } 16 17 export const TableCellOverflow: FunctionComponent<any> = ({ children, ...props }) => { 18 19 const cellRef = useRef<HTMLTableCellElement>(null); 20 const [overflow, setOverflow] = useState<boolean>(false); 21 22 useEffect(() => { 23 if(cellRef.current) { 24 setOverflow(cellRef.current.scrollWidth > cellRef.current.clientWidth) 25 } 26 }, []) 27 28 return ( 29 <TableCell 30 ref={cellRef} 31 sx={{ 32 overflowX: "scroll", 33 whiteSpace: "nowrap", 34 boxShadow: overflow ? "inset -13px 0px 20px -21px rgba(0,0,0,0.75)" : "none", 35 ...props?.sx 36 }}> 37 {children} 38 </TableCell> 39 ) 40 } 41 42 export const RecordTable = ({ data }: { data: Record[] }): ReactElement => { 43 return ( 44 <TableContainer> 45 <Table sx={{tableLayout: "fixed"}}> 46 <TableHead> 47 <TableRow> 48 {Object.keys(data[0]).map((key, index) => ( 49 <TableCell key={index} sx={{width: index == 0 ? "20%" : "40%"}}>{key}</TableCell> 50 ))} 51 </TableRow> 52 </TableHead> 53 <TableBody> 54 {data.map((record, index) => ( 55 <TableRow key={index}> 56 {Object.values(record).map((value, index) => ( 57 <TableCellOverflow key={index} sx={{width: index == 0 ? "20%" : "40%"}}>{value == null ? "NULL" : value}</TableCellOverflow> 58 ))} 59 </TableRow> 60 ))} 61 </TableBody> 62 </Table> 63 </TableContainer> 64 ) 65 } 66 67 68 export const DataExportTable = () => { 69 70 const [data, setData] = useState<ExportData[] | undefined>(undefined); 71 const [error, setError] = useState<string | undefined>(undefined); 72 73 74 const getData = async () => { 75 let response = await fetch("/api/v1.0/config") 76 if (response.ok) { 77 const responseData = await response.json() 78 79 setData([{ 80 "Type": "POSIX", 81 "Local Path": ["", undefined].includes(responseData?.Xrootd?.Mount) ? "NULL" : responseData?.Xrootd?.Mount, 82 "Namespace Prefix": ["", undefined].includes(responseData?.Origin?.NamespacePrefix) ? "NULL" : responseData?.Origin?.NamespacePrefix 83 }]) 84 85 } else { 86 setError("Failed to fetch config, response status: " + response.status) 87 } 88 } 89 90 useEffect(() => { 91 getData() 92 }, []) 93 94 if(error){ 95 return ( 96 <Box p={1}> 97 <Typography sx={{color: "red"}} variant={"subtitle2"}>{error}</Typography> 98 </Box> 99 ) 100 } 101 102 return ( 103 <> 104 {data ? <RecordTable data={data} /> : <Skeleton variant={"rectangular"} height={200} width={"100%"} />} 105 </> 106 ) 107 }