Skip to content

Commit

Permalink
task/DES-2924: Add metadata and move/copy/download buttons to preview…
Browse files Browse the repository at this point in the history
… modal (#1334)

* Add metadata and move/copy/download buttons to preview modal

* formatting
  • Loading branch information
jarosenb authored Jul 8, 2024
1 parent d67ca44 commit f8dd741
Show file tree
Hide file tree
Showing 8 changed files with 200 additions and 39 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { TModalChildren } from '../DatafilesModal';
import { Alert, Button, Modal, Table } from 'antd';
import {
TFileListing,
useCheckFilesForAssociation,
useFileMove,
usePathDisplayName,
useSelectedFiles,
} from '@client/hooks';

import {
Expand Down Expand Up @@ -122,15 +122,15 @@ export const MoveModal: React.FC<{
api: string;
system: string;
path: string;
selectedFiles: TFileListing[];
succesCallback?: CallableFunction;
children: TModalChildren;
}> = ({ api, system, path, children }) => {
}> = ({ api, system, path, selectedFiles, succesCallback, children }) => {
const [isModalOpen, setIsModalOpen] = useState(false);

const showModal = () => setIsModalOpen(true);
const handleClose = () => setIsModalOpen(false);

const { selectedFiles } = useSelectedFiles(api, system, path);

let { projectId } = useParams();
if (!projectId) projectId = '';

Expand Down Expand Up @@ -170,14 +170,17 @@ export const MoveModal: React.FC<{
const mutateCallback = useCallback(
(dPath: string) => {
selectedFiles.forEach((f) =>
mutate({
src: { api, system, path: encodeURIComponent(f.path) },
dest: { api: destApi, system: destSystem, path: dPath },
})
mutate(
{
src: { api, system, path: encodeURIComponent(f.path) },
dest: { api: destApi, system: destSystem, path: dPath },
},
{ onSuccess: () => succesCallback && succesCallback() }
)
);
handleClose();
},
[selectedFiles, mutate, destApi, destSystem, api, system]
[selectedFiles, mutate, destApi, destSystem, succesCallback, api, system]
);

const DestFilesColumns = useMemo(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { Collapse, Table, TableProps } from 'antd';
import React from 'react';
import styles from './PreviewModal.module.css';
import { TFileListing, useFileDetail } from '@client/hooks';
import { toBytes } from '../../FileListing/FileListing';

const tableColumns: TableProps['columns'] = [
{ dataIndex: 'key', render: (value) => <strong>{value}</strong>, width: 200 },
{ dataIndex: 'value' },
];

export const PreviewMetadata: React.FC<{
selectedFile: TFileListing;
fileMeta: Record<string, string>;
}> = ({ selectedFile, fileMeta }) => {
const { data: fileListingMeta } = useFileDetail(
'tapis',
selectedFile.system,
'private',
selectedFile.path
);

const baseListingMeta = [
{ key: 'File Name', value: fileListingMeta?.name },
{ key: 'File Path', value: fileListingMeta?.path },
{ key: 'File Size', value: toBytes(fileListingMeta?.length) },
{
key: 'Last Modified',
value:
fileListingMeta?.lastModified &&
new Date(fileListingMeta.lastModified).toLocaleString(),
},
];

const fullListingMeta = [
...baseListingMeta,
...Object.keys(fileMeta).map((k) => ({ key: k, value: fileMeta[k] })),
];

return (
<Collapse
className={styles.metadataCollapse}
expandIconPosition="end"
items={[
{
label: (
<span style={{ fontStyle: 'italic', fontWeight: '600' }}>
File Metadata
</span>
),
children: (
<Table
columns={tableColumns}
dataSource={fullListingMeta}
pagination={false}
/>
),
},
]}
/>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,18 @@
.previewContainer img {
width: 100%;
}

.metadataCollapse {
background-color: 'black';
}

.metadataCollapse :global(.ant-collapse-content-box) {
padding: 0px !important;
}
.metadataCollapse :global(.ant-table-thead) {
display: none;
}

.metadataCollapse :global(.ant-table-tbody) tr:nth-child(odd) td {
background-color: white;
}
Original file line number Diff line number Diff line change
@@ -1,19 +1,27 @@
import { useQueryClient } from '@tanstack/react-query';
import { useFilePreview } from '@client/hooks';
import {
TFileListing,
useFileListingRouteParams,
useFilePreview,
} from '@client/hooks';
import { Button, Modal } from 'antd';
import React, { useCallback, useState } from 'react';
import styles from './PreviewModal.module.css';
import { TModalChildren } from '../DatafilesModal';
import { PreviewSpinner, PreviewContent } from './PreviewContent';
import { PreviewMetadata } from './PreviewMetadata';
import { CopyModal } from '../CopyModal';
import { DownloadModal } from '../DownloadModal';
import { MoveModal } from '../MoveModal';

export const PreviewModalBody: React.FC<{
isOpen: boolean;
api: string;
system: string;
scheme?: string;
path: string;

selectedFile: TFileListing;
handleCancel: () => void;
}> = ({ isOpen, api, system, scheme, path, handleCancel }) => {
}> = ({ isOpen, api, scheme, selectedFile, handleCancel }) => {
/*
Typically modals are rendered in the same component as the button that manages the
open/closed state. The modal body is exported separately for file previews, since
Expand All @@ -22,12 +30,13 @@ export const PreviewModalBody: React.FC<{
const queryClient = useQueryClient();
const { data, isLoading } = useFilePreview({
api,
system,
system: selectedFile.system,
scheme,
path,
path: selectedFile.path,
queryOptions: { enabled: isOpen },
});

const { path: listingPath } = useFileListingRouteParams();
const handleClose = useCallback(() => {
// Flush queries on close to prevent stale postits being read from cache.
queryClient.removeQueries({ queryKey: ['datafiles', 'preview'] });
Expand All @@ -38,7 +47,7 @@ export const PreviewModalBody: React.FC<{

return (
<Modal
title={<h2>File Preview: {path}</h2>}
title={<h2>File Preview: {selectedFile.path.split('/').slice(-1)}</h2>}
width="60%"
open={isOpen}
footer={() => (
Expand All @@ -48,6 +57,61 @@ export const PreviewModalBody: React.FC<{
)}
onCancel={handleClose}
>
<PreviewMetadata
selectedFile={selectedFile}
fileMeta={data?.fileMeta ?? {}}
/>
<div
style={{
display: 'flex',
marginTop: '10px',
gap: '10px',
justifyContent: 'center',
}}
>
{scheme === 'private' && api === 'tapis' && (
<MoveModal
api={api}
system={selectedFile.system}
path={listingPath}
selectedFiles={[selectedFile]}
succesCallback={handleCancel}
>
{({ onClick }) => (
<Button onClick={onClick}>
<i role="none" className="fa fa-arrows" />
<span>&nbsp;Move</span>
</Button>
)}
</MoveModal>
)}
<CopyModal
api={api}
system={selectedFile.system}
path={listingPath}
selectedFiles={[selectedFile]}
>
{({ onClick }) => (
<Button onClick={onClick}>
<i role="none" className="fa fa-copy" />
<span>&nbsp;Copy</span>
</Button>
)}
</CopyModal>

<DownloadModal
api={api}
system={selectedFile.system}
selectedFiles={[selectedFile]}
>
{({ onClick }) => (
<Button onClick={onClick}>
<i role="none" className="fa fa-cloud-download" />
<span>&nbsp;Download</span>
</Button>
)}
</DownloadModal>
</div>
<div className={styles.modalContentContainer}>
{isLoading && <PreviewSpinner />}
{data && isOpen && (
Expand All @@ -63,16 +127,14 @@ export const PreviewModalBody: React.FC<{

type TPreviewModal = React.FC<{
api: string;
system: string;
scheme?: string;
path: string;
selectedFile: TFileListing;
children: TModalChildren;
}>;
export const PreviewModal: TPreviewModal = ({
api,
system,
scheme,
path,
selectedFile,
children,
}) => {
const [isModalOpen, setIsModalOpen] = useState(false);
Expand All @@ -91,9 +153,8 @@ export const PreviewModal: TPreviewModal = ({
{isModalOpen && (
<PreviewModalBody
api={api}
system={system}
scheme={scheme}
path={path}
selectedFile={selectedFile}
isOpen={isModalOpen}
handleCancel={handleCancel}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,12 @@ export const DatafilesToolbar: React.FC<{ searchInput?: React.ReactNode }> = ({
)}
</DatafilesModal.Rename>

<DatafilesModal.Move api={api} system={system} path={path}>
<DatafilesModal.Move
api={api}
system={system}
path={path}
selectedFiles={selectedFiles}
>
{({ onClick }) => (
<ToolbarButton
onClick={onClick}
Expand All @@ -122,9 +127,8 @@ export const DatafilesToolbar: React.FC<{ searchInput?: React.ReactNode }> = ({
</DatafilesModal.Move>
<DatafilesModal.Preview
api={api}
system={system}
scheme={scheme}
path={selectedFiles[0]?.path ?? ''}
selectedFile={selectedFiles[0]}
>
{({ onClick }) => (
<ToolbarButton
Expand Down
17 changes: 11 additions & 6 deletions client/modules/datafiles/src/FileListing/FileListing.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
} from '@client/common-components';
import { NavLink } from 'react-router-dom';
import { PreviewModalBody } from '../DatafilesModal/PreviewModal';
import { TFileTag } from '@client/hooks';
import { TFileListing, TFileTag } from '@client/hooks';

export function toBytes(bytes?: number) {
if (bytes === 0) return '0 bytes';
Expand Down Expand Up @@ -42,6 +42,7 @@ export const FileListing: React.FC<
const [previewModalState, setPreviewModalState] = useState<{
isOpen: boolean;
path?: string;
selectedFile?: TFileListing;
}>({ isOpen: false });

const columns: TFileListingColumns = useMemo(
Expand Down Expand Up @@ -76,7 +77,11 @@ export const FileListing: React.FC<
type="link"
style={{ userSelect: 'text' }}
onClick={() =>
setPreviewModalState({ isOpen: true, path: record.path })
setPreviewModalState({
isOpen: true,
path: record.path,
selectedFile: record,
})
}
>
{data}
Expand Down Expand Up @@ -106,7 +111,7 @@ export const FileListing: React.FC<
render: (d) => new Date(d).toLocaleString(),
},
],
[setPreviewModalState, baseRoute]
[setPreviewModalState, baseRoute, fileTags]
);

return (
Expand All @@ -119,12 +124,12 @@ export const FileListing: React.FC<
columns={columns}
{...tableProps}
/>
{previewModalState.path && (
{previewModalState.path && previewModalState.selectedFile && (
<PreviewModalBody
scheme={scheme}
selectedFile={previewModalState.selectedFile}
isOpen={previewModalState.isOpen}
api={api}
system={system}
path={previewModalState.path}
handleCancel={() => setPreviewModalState({ isOpen: false })}
/>
)}
Expand Down
Loading

0 comments on commit f8dd741

Please sign in to comment.