diff --git a/packages/core/components/Modal/CopyFileManifest/CopyFileManifest.module.css b/packages/core/components/Modal/CopyFileManifest/CopyFileManifest.module.css index 624ed4b1..48482b9b 100644 --- a/packages/core/components/Modal/CopyFileManifest/CopyFileManifest.module.css +++ b/packages/core/components/Modal/CopyFileManifest/CopyFileManifest.module.css @@ -2,16 +2,49 @@ position: relative; } +.note { + margin-top: 0.5em; + margin-bottom: 1em; +} + +.tableContainer { + margin-bottom: 2em; +} + +.tableTitle { + font-size: 16px; + font-weight: 600; + margin: 4px 0 8px 0; +} + +.tableWrapper { + position: relative; +} + .fileTableContainer { max-height: 300px; overflow-y: auto; - margin-bottom: 0.5em; - background: linear-gradient(to bottom, transparent, var(--secondary-background-color)); + background-color: var(--secondary-background-color); + position: relative; + z-index: 1; +} + +.gradientOverlay { + position: absolute; + bottom: 0; + left: 0; + width: 100%; + height: 50px; + background-image: linear-gradient(transparent, black); + pointer-events: none; + z-index: 2; } .file-table { width: 100%; border-collapse: collapse; + position: relative; + z-index: 1; } .file-table th, @@ -20,13 +53,14 @@ text-align: left; white-space: nowrap; color: var(--primary-text-color); + background-color: var(--secondary-color); } .file-table th { background-color: var(--secondary-background-color); position: sticky; top: 0; - z-index: 1; + z-index: 3; } .file-table td:first-child { @@ -57,4 +91,3 @@ .fileCount { text-align: right; } - diff --git a/packages/core/components/Modal/CopyFileManifest/index.tsx b/packages/core/components/Modal/CopyFileManifest/index.tsx index 3e8b345f..c18e1bd8 100644 --- a/packages/core/components/Modal/CopyFileManifest/index.tsx +++ b/packages/core/components/Modal/CopyFileManifest/index.tsx @@ -11,51 +11,101 @@ import { interaction, selection } from "../../../state"; import styles from "./CopyFileManifest.module.css"; +/** + * Table component for rendering file details. + */ +function FileTable({ files, title }: { files: FileDetail[]; title: string }) { + const containerRef = React.useRef(null); + const [hasScroll, setHasScroll] = React.useState(false); + + React.useEffect(() => { + const checkScroll = () => { + if (containerRef.current) { + const isScrollable = + containerRef.current.scrollHeight > containerRef.current.clientHeight; + setHasScroll(isScrollable); + } + }; + checkScroll(); // Initial check + window.addEventListener("resize", checkScroll); + return () => window.removeEventListener("resize", checkScroll); + }, [files]); + + const clipFileName = (filename: string) => { + if (filename.length > 20) { + return filename.slice(0, 9) + "..." + filename.slice(-8); + } + return filename; + }; + + const calculateTotalSize = (files: FileDetail[]) => { + if (files.length === 0) return ""; + const totalBytes = files.reduce((acc, file) => acc + (file.size || 0), 0); + return totalBytes ? filesize(totalBytes) : "Calculating..."; + }; + + return ( +
+

{title}

+
+
+ + + + + + + + + {files.map((file) => ( + + + + + ))} + +
File NameFile Size
{clipFileName(file.name)}{filesize(file.size || 0)}
+
+ {hasScroll &&
} +
+
+ {files.length > 0 && ( + {calculateTotalSize(files)} + )} + {files.length.toLocaleString()} files +
+
+ ); +} + /** * Modal overlay for displaying details of selected files for NAS cache operations. */ export default function CopyFileManifest({ onDismiss }: ModalProps) { const dispatch = useDispatch(); - const fileService = useSelector(interaction.selectors.getFileService); const fileSelection = useSelector( selection.selectors.getFileSelection, FileSelection.selectionsAreEqual ); const [fileDetails, setFileDetails] = React.useState([]); - const [totalSize, setTotalSize] = React.useState(); - const [isLoading, setLoading] = React.useState(false); - - // Utility function to clip file names - const clipFileName = (filename: string) => { - if (filename.length > 20) { - return filename.slice(0, 9) + "..." + filename.slice(-8); - } - return filename; - }; React.useEffect(() => { async function fetchDetails() { - setLoading(true); const details = await fileSelection.fetchAllDetails(); setFileDetails(details); - - const aggregateInfo = await fileService.getAggregateInformation(fileSelection); - const formattedSize = aggregateInfo.size ? filesize(aggregateInfo.size) : undefined; - setTotalSize(formattedSize); - setLoading(false); } - fetchDetails(); - }, [fileSelection, fileService]); + }, [fileSelection]); - // Handler for moving files to NAS cache const onMove = () => { dispatch(interaction.actions.copyFiles(fileDetails)); onDismiss(); }; - // Separate files by "Should Be in Local Cache" const filesInLocalCache = fileDetails.filter((file) => file.annotations.some( (annotation) => @@ -63,63 +113,39 @@ export default function CopyFileManifest({ onDismiss }: ModalProps) { ) ); - const filesNotInLocalCache = fileDetails.filter((file) => - file.annotations.some( - (annotation) => - annotation.name === "Should Be in Local Cache" && annotation.values[0] === false - ) - ); - - // Reusable function to render a table for files - const renderTable = (files: FileDetail[], title: string) => ( -
-

{title}

-
- - - - - - - - - {files.map((file) => ( - - - - - ))} - -
File NameFile Size
{clipFileName(file.name)}{filesize(file.size || 0)}
-
-
- ); - - const body = ( -
-

- Files copied to the local NAS (Vast) are stored with a 180-day expiration, after - which they revert to cloud-only. To extend the expiration, simply reselect the files - and confirm the update. -

- {renderTable(filesInLocalCache, "Files that are already on Vast: Extend expiration")} - {renderTable(filesNotInLocalCache, "Files to download to Vast")} -
- - {isLoading ? "Calculating..." : totalSize || "0 B"} - - - {fileDetails.length.toLocaleString()} files - -
-
+ const filesNotInLocalCache = fileDetails.filter( + (file) => + file.annotations.some( + (annotation) => + annotation.name === "Should Be in Local Cache" && annotation.values[0] === false + ) || + !file.annotations.some((annotation) => annotation.name === "Should Be in Local Cache") ); return ( +

+ Files copied to the local NAS (VAST) are stored with a 180-day expiration, + after which they revert to cloud-only storage. To extend the expiration, + reselect the files and confirm the update. +

+ + +
+ } footer={
+ -
} onDismiss={onDismiss} - title="Copy Files to Local NAS (Vast)" + title="Copy files to local NAS (VAST)" /> ); }