Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature/move-files-styling #320

Merged
merged 7 commits into from
Dec 20, 2024
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
.bodyContainer {
position: relative;
}

.fileTableContainer {
max-height: 300px;
overflow-y: auto;
border: 1px solid var(--border-color);
margin-bottom: 1em;
padding: 1em;
margin-bottom: 0.5em;
background: linear-gradient(to bottom, transparent, var(--secondary-background-color));
}

Expand All @@ -16,7 +18,6 @@
.file-table td {
padding: 8px;
text-align: left;
border-bottom: 1px solid var(--border-color);
white-space: nowrap;
color: var(--primary-text-color);
}
Expand All @@ -31,3 +32,29 @@
.file-table td:first-child {
border-right: 1px solid var(--border-color);
}

.footerButtons {
display: flex;
justify-content: flex-end;
gap: 8px;
}

.summary {
display: flex;
justify-content: flex-end;
gap: 1em;
margin-top: var(--row-count-margin-top);
color: var(--secondary-text-color);
opacity: 0.8;
font-size: var(--row-count-intrisic-height);
height: var(--row-count-intrisic-height-height);
}

.totalSize {
text-align: right;
}

.fileCount {
text-align: right;
}

142 changes: 142 additions & 0 deletions packages/core/components/Modal/CopyFileManifest/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
import filesize from "filesize";
import * as React from "react";
import { useDispatch, useSelector } from "react-redux";

import { ModalProps } from "..";
import BaseModal from "../BaseModal";
import { PrimaryButton, SecondaryButton } from "../../Buttons";
import FileDetail from "../../../entity/FileDetail";
import FileSelection from "../../../entity/FileSelection";
import { interaction, selection } from "../../../state";

import styles from "./CopyFileManifest.module.css";

/**
* 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<FileDetail[]>([]);
const [totalSize, setTotalSize] = React.useState<string | undefined>();
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]);

// 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) =>
annotation.name === "Should Be in Local Cache" && annotation.values[0] === true
)
);

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) => (
<div>
<h3 className={styles.tableTitle}>{title}</h3>
<div className={styles.fileTableContainer}>
<table className={styles.fileTable}>
<thead>
<tr>
<th>File Name</th>
<th>File Size</th>
</tr>
</thead>
<tbody>
{files.map((file) => (
<tr key={file.id}>
<td>{clipFileName(file.name)}</td>
<td>{filesize(file.size || 0)}</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
);

const body = (
<div className={styles.bodyContainer}>
<p className={styles.note}>
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.
</p>
{renderTable(filesInLocalCache, "Files that are already on Vast: Extend expiration")}
{renderTable(filesNotInLocalCache, "Files to download to Vast")}
<div className={styles.summary}>
<span className={styles.totalSize}>
{isLoading ? "Calculating..." : totalSize || "0 B"}
</span>
<span className={styles.fileCount}>
{fileDetails.length.toLocaleString()} files
</span>
</div>
</div>
);

return (
<BaseModal
body={body}
footer={
<div className={styles.footerButtons}>
<PrimaryButton
className={styles.confirmButton}
disabled={!fileDetails.length}
onClick={onMove}
text="CONFIRM"
title=""
/>
<SecondaryButton
className={styles.cancelButton}
onClick={onDismiss}
text="CANCEL"
title=""
/>
</div>
}
onDismiss={onDismiss}
title="Copy Files to Local NAS (Vast)"
/>
);
}
92 changes: 0 additions & 92 deletions packages/core/components/Modal/MoveFileManifest/index.tsx

This file was deleted.

8 changes: 4 additions & 4 deletions packages/core/components/Modal/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import CodeSnippet from "./CodeSnippet";
import DataSource from "./DataSource";
import MetadataManifest from "./MetadataManifest";
import SmallScreenWarning from "./SmallScreenWarning";
import MoveFileManifest from "./MoveFileManifest";
import CopyFileManifest from "./CopyFileManifest";

export interface ModalProps {
onDismiss: () => void;
Expand All @@ -17,7 +17,7 @@ export enum ModalType {
DataSource = 2,
MetadataManifest = 3,
SmallScreenWarning = 4,
MoveFileManifest = 5,
CopyFileManifest = 5,
}

/**
Expand All @@ -40,8 +40,8 @@ export default function Modal() {
return <MetadataManifest onDismiss={onDismiss} />;
case ModalType.SmallScreenWarning:
return <SmallScreenWarning onDismiss={onDismiss} />;
case ModalType.MoveFileManifest:
return <MoveFileManifest onDismiss={onDismiss} />;
case ModalType.CopyFileManifest:
return <CopyFileManifest onDismiss={onDismiss} />;
default:
return null;
}
Expand Down
8 changes: 4 additions & 4 deletions packages/core/hooks/useFileAccessContextMenu.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,13 +138,13 @@ export default (filters?: FileFilter[], onDismiss?: () => void) => {
...(isQueryingAicsFms && !isOnWeb
? [
{
key: "move-to-cache",
text: "Move to Cache",
title: "Move selected files to NAS Cache",
key: "copy-to-cache",
text: "Copy to vast",
title: "Copy selected files to NAS Cache (VAST)",
disabled: !filters && fileSelection.count() === 0,
iconProps: { iconName: "MoveToFolder" },
onClick() {
dispatch(interaction.actions.showMoveFileManifest());
dispatch(interaction.actions.showCopyFileManifest());
},
},
]
Expand Down
22 changes: 11 additions & 11 deletions packages/core/state/interaction/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -679,35 +679,35 @@ export function setSelectedPublicDataset(dataset: PublicDataset): SetSelectedPub
}

/**
* SHOW_MOVE_FILE_MANIFEST
* SHOW_COPY_FILE_MANIFEST
*
* Action to show the Move File dialog (manifest) for NAS cache operations.
* This modal will allow users to move files onto the NAS cache.
* Action to show the Copy File dialog (manifest) for NAS cache operations.
* This modal will allow users to copy files onto the NAS cache.
*/
export const SHOW_MOVE_FILE_MANIFEST = makeConstant(STATE_BRANCH_NAME, "show-move-file-manifest");
export const SHOW_COPY_FILE_MANIFEST = makeConstant(STATE_BRANCH_NAME, "show-copy-file-manifest");

export interface ShowMoveFileManifestAction {
export interface ShowCopyFileManifestAction {
type: string;
}

export function showMoveFileManifest(): ShowMoveFileManifestAction {
export function showCopyFileManifest(): ShowCopyFileManifestAction {
return {
type: SHOW_MOVE_FILE_MANIFEST,
type: SHOW_COPY_FILE_MANIFEST,
};
}

export const MOVE_FILES = makeConstant(STATE_BRANCH_NAME, "move-files");
export const COPY_FILES = makeConstant(STATE_BRANCH_NAME, "copy-files");

export interface MoveFilesAction {
export interface CopyFilesAction {
type: string;
payload: {
fileDetails: FileDetail[];
};
}

export function moveFiles(fileDetails: FileDetail[]): MoveFilesAction {
export function copyFiles(fileDetails: FileDetail[]): CopyFilesAction {
return {
type: MOVE_FILES,
type: COPY_FILES,
payload: {
fileDetails,
},
Expand Down
Loading
Loading