frontend/app/ProjectEntryList/ProjectEntryVaultComponent.tsx (191 lines of code) (raw):

import React, { useEffect, useState } from "react"; import { Paper, Typography, TableContainer, Table, TableHead, TableRow, TableBody, TableCell, Tooltip, IconButton, Collapse, Grid, CircularProgress, } from "@material-ui/core"; import { authenticatedFetch } from "../common/auth"; import KeyboardArrowUpIcon from "@material-ui/icons/KeyboardArrowUp"; import KeyboardArrowDownIcon from "@material-ui/icons/KeyboardArrowDown"; import { loadAllVaultData, VaultState } from "../vaultdoor/vaultdoor"; import { useGuardianStyles } from "~/misc/utils"; declare var vaultdoorURL: string; interface ProjectEntryVaultComponentProps { project: Project; onError?: (errorDesc: string) => void; } const ProjectEntryVaultComponent: React.FC<ProjectEntryVaultComponentProps> = ( props ) => { const classes = useGuardianStyles(); const [loading, setLoading] = useState(false); const [didLoad, setDidLoad] = useState(false); const { project } = props; const [knownVaults, setKnownVaults] = useState<Array<VaultDescription>>([]); const [data, setData] = useState<VaultState[]>([]); const [open, setOpen] = useState<boolean>(false); const [vaultDataPresent, setVaultDataPresent] = useState<boolean>(false); const fetchVaults = async () => { const response = await authenticatedFetch(`${vaultdoorURL}api/vault`, {}); switch (response.status) { case 200: const content = (await response.json()) as Array<VaultDescription>; if (Array.isArray(content)) { const reversed = content.reverse(); setKnownVaults(reversed); } else { console.error( "Expected server response to be an array, got ", content ); if (props.onError) { props.onError( "Could not load archive data, see console for details" ); } setLoading(false); } break; default: const errorContent = await response.text(); console.error(errorContent); if (props.onError) props.onError("Could not load archive data, see console for details"); setLoading(false); break; } }; const isVaultDataPresent = () => { data.map((vault) => { if (vault.fileCount != 0 && vaultDataPresent == false) { setVaultDataPresent(true); } }); }; useEffect(() => { if (open && !didLoad) { setLoading(true); fetchVaults().catch((err) => { console.error("Could not load vaults: ", err); if (props.onError) props.onError("Could not load archive data, see console for details"); }); } }, [open]); useEffect(() => { if (knownVaults.length == 0) return; //don't bother to attempt data load if there is nothing to load in... console.log(`Loading in data for ${knownVaults.length} vaults...`); loadAllVaultData(vaultdoorURL, project, knownVaults) .then((loadedData) => { setData(loadedData); setDidLoad(true); }) .catch((err) => { console.error("Could not load data: ", err); if (props.onError) props.onError( "Could not load in storage data, see console for details" ); }); }, [knownVaults]); useEffect(() => { isVaultDataPresent(); setLoading(false); }, [data]); const humanFileSize = (bytes: number, si = false, dp = 1) => { const thresh = si ? 1000 : 1024; if (Math.abs(bytes) < thresh) { return bytes + " B"; } const units = si ? ["kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"] : ["KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"]; let u = -1; const r = 10 ** dp; do { bytes /= thresh; ++u; } while ( Math.round(Math.abs(bytes) * r) / r >= thresh && u < units.length - 1 ); return bytes.toFixed(dp) + " " + units[u]; }; return ( <Paper className={classes.projectVaultData}> <Grid container spacing={3} justifyContent="space-between"> <Grid item> <Typography variant="h4">Storage</Typography> </Grid> <Grid item> <Tooltip title="Show archived data"> <IconButton className={classes.archiveButton} aria-label="expand data" size="small" id="archive-expander-button" onClick={() => setOpen(!open)} > {open ? ( <KeyboardArrowUpIcon className={classes.archiveIcon} /> ) : ( <KeyboardArrowDownIcon className={classes.archiveIcon} /> )} </IconButton> </Tooltip> </Grid> </Grid> <Collapse in={open} timeout="auto" unmountOnExit> {loading ? ( <CircularProgress id="loading-spinner" /> ) : vaultDataPresent ? ( <TableContainer id="vaults-table"> <Table> <TableHead> <TableRow> <TableCell>Vault</TableCell> <TableCell>File Count</TableCell> <TableCell>Data Size</TableCell> </TableRow> </TableHead> <TableBody> {data .filter((v) => v.fileCount > 0) .map((vault, idx) => ( <TableRow id={`data-${vault.vaultName.replace(" ", "-")}`} hover={true} onClick={() => { window.open( `${vaultdoorURL}byproject?project=${project.id}`, "_blank" ); }} key={idx} > <TableCell>{vault.vaultName}</TableCell> <TableCell>{vault.fileCount}</TableCell> <TableCell>{humanFileSize(vault.totalSize)}</TableCell> </TableRow> ))} </TableBody> </Table> </TableContainer> ) : ( <div>No locally archived data present for this project.</div> )} </Collapse> </Paper> ); }; export default ProjectEntryVaultComponent;