Skip to content

Commit

Permalink
feat: add file size and usage metrics (#573)
Browse files Browse the repository at this point in the history
  • Loading branch information
KristinAoki authored Aug 31, 2023
1 parent ffae3bd commit e50b8c7
Show file tree
Hide file tree
Showing 13 changed files with 369 additions and 172 deletions.
50 changes: 30 additions & 20 deletions src/files-and-uploads/FileInfo.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React, { useState } from 'react';
import PropTypes from 'prop-types';

import {
injectIntl,
FormattedMessage,
Expand All @@ -17,37 +18,44 @@ import {
CheckboxControl,
} from '@edx/paragon';
import { ContentCopy, InfoOutline } from '@edx/paragon/icons';
import AssetThumbnail from './FileThumbnail';

import { getFileSizeToClosestByte } from './data/utils';
import AssetThumbnail from './FileThumbnail';
import messages from './messages';
import UsageMetricsMessages from './UsageMetricsMessage';

const FileInfo = ({
asset,
isOpen,
onClose,
handleLockedAsset,
usagePathStatus,
error,
// injected
intl,
}) => {
const [lockedState, setLockedState] = useState(asset.locked);
const [lockedState, setLockedState] = useState(asset?.locked);
const handleLock = (e) => {
const locked = e.target.checked;
setLockedState(locked);
handleLockedAsset(asset.id, locked);
handleLockedAsset(asset?.id, locked);
};
const fileSize = getFileSizeToClosestByte(asset?.fileSize);

return (
<ModalDialog
title={asset.displayName}
title={asset?.displayName}
isOpen={isOpen}
onClose={onClose}
size="lg"
hasCloseButton
data-testid="file-info-modal"
>
<ModalDialog.Header>
<ModalDialog.Title>
<div style={{ wordBreak: 'break-word' }}>
<Truncate lines={2} className="font-weight-bold small mt-3">
{asset.displayName}
{asset?.displayName}
</Truncate>
</div>
</ModalDialog.Title>
Expand All @@ -57,18 +65,18 @@ const FileInfo = ({
<div className="row flex-nowrap m-0 mt-4">
<div className="col-8 mr-3">
<AssetThumbnail
thumbnail={asset.thumbnail}
externalUrl={asset.externalUrl}
displayName={asset.displayName}
wrapperType={asset.wrapperType}
thumbnail={asset?.thumbnail}
externalUrl={asset?.externalUrl}
displayName={asset?.displayName}
wrapperType={asset?.wrapperType}
/>
</div>
<Stack>
<div className="font-weight-bold">
<FormattedMessage {...messages.dateAddedTitle} />
</div>
<FormattedDate
value={asset.dateAdded}
value={asset?.dateAdded}
year="numeric"
month="short"
day="2-digit"
Expand All @@ -78,23 +86,22 @@ const FileInfo = ({
<div className="font-weight-bold mt-3">
<FormattedMessage {...messages.fileSizeTitle} />
</div>
{/* {asset.fileSize} */}
<hr />
<div className="font-weight-bold mt-3">
{fileSize}
<div className="font-weight-bold border-top mt-3 pt-3">
<FormattedMessage {...messages.studioUrlTitle} />
</div>
<ActionRow>
<div style={{ wordBreak: 'break-word' }}>
<Truncate lines={1}>
{asset.portableUrl}
{asset?.portableUrl}
</Truncate>
</div>
<ActionRow.Spacer />
<IconButton
src={ContentCopy}
iconAs={Icon}
alt={messages.copyStudioUrlTitle.defaultMessage}
onClick={() => navigator.clipboard.writeText(asset.portableUrl)}
onClick={() => navigator.clipboard.writeText(asset?.portableUrl)}
/>
</ActionRow>
<div className="font-weight-bold mt-3">
Expand All @@ -103,19 +110,18 @@ const FileInfo = ({
<ActionRow>
<div style={{ wordBreak: 'break-word' }}>
<Truncate lines={1}>
{asset.externalUrl}
{asset?.externalUrl}
</Truncate>
</div>
<ActionRow.Spacer />
<IconButton
src={ContentCopy}
iconAs={Icon}
alt={messages.copyWebUrlTitle.defaultMessage}
onClick={() => navigator.clipboard.writeText(asset.externalUrl)}
onClick={() => navigator.clipboard.writeText(asset?.externalUrl)}
/>
</ActionRow>
<hr />
<ActionRow>
<ActionRow className=" border-top mt-3 pt-3">
<div className="font-weight-bold">
<FormattedMessage {...messages.lockFileTitle} />
</div>
Expand All @@ -140,11 +146,11 @@ const FileInfo = ({
<div className="row m-0 pt-3 font-weight-bold">
<FormattedMessage {...messages.usageTitle} />
</div>
<UsageMetricsMessages {...{ usageLocations: asset?.usageLocations, usagePathStatus, error }} />
</ModalDialog.Body>
</ModalDialog>
);
};

FileInfo.propTypes = {
asset: PropTypes.shape({
displayName: PropTypes.string.isRequired,
Expand All @@ -155,10 +161,14 @@ FileInfo.propTypes = {
id: PropTypes.string.isRequired,
portableUrl: PropTypes.string.isRequired,
dateAdded: PropTypes.string.isRequired,
fileSize: PropTypes.number.isRequired,
usageLocations: PropTypes.arrayOf(PropTypes.string),
}).isRequired,
onClose: PropTypes.func.isRequired,
isOpen: PropTypes.bool.isRequired,
handleLockedAsset: PropTypes.func.isRequired,
usagePathStatus: PropTypes.string.isRequired,
error: PropTypes.arrayOf(PropTypes.string).isRequired,
// injected
intl: intlShape.isRequired,
};
Expand Down
75 changes: 49 additions & 26 deletions src/files-and-uploads/FilesAndUploads.jsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React, { useEffect, useState } from 'react';
import PropTypes from 'prop-types';
import { useDispatch, useSelector } from 'react-redux';
import _ from 'lodash';
import isEmpty from 'lodash/isEmpty';
import { injectIntl, FormattedMessage, intlShape } from '@edx/frontend-platform/i18n';
import {
DataTable,
Expand All @@ -22,12 +22,14 @@ import {
addAssetFile,
deleteAssetFile,
fetchAssets,
getUsagePaths,
updateAssetLock,
updateAssetOrder,
} from './data/thunks';
import { sortFiles } from './data/utils';
import messages from './messages';

import FileInfo from './FileInfo';
import FileInput, { fileInput } from './FileInput';
import FilesAndUploadsProvider from './FilesAndUploadsProvider';
import {
Expand All @@ -52,6 +54,7 @@ const FilesAndUploads = ({
};
const [currentView, setCurrentView] = useState(defaultVal);
const [isDeleteOpen, setDeleteOpen, setDeleteClose] = useToggle(false);
const [isAssetInfoOpen, openAssetInfo, closeAssetinfo] = useToggle(false);
const [isAddOpen, setAddOpen, setAddClose] = useToggle(false);
const [selectedRows, setSelectedRows] = useState([]);
const [isDeleteConfirmationOpen, openDeleteConfirmation, closeDeleteConfirmation] = useToggle(false);
Expand All @@ -65,9 +68,10 @@ const FilesAndUploads = ({
loadingStatus,
addingStatus: addAssetStatus,
deletingStatus: deleteAssetStatus,
savingStatus: saveAssetStatus,
updatingStatus: updateAssetStatus,
usageStatus: usagePathStatus,
errors: errorMessages,
} = useSelector(state => state.assets);
const errorMessages = useSelector(state => state.assets.errors);
const fileInputControl = fileInput({
onAddFile: (file) => dispatch(addAssetFile(courseId, file, totalCount)),
setSelectedRows,
Expand Down Expand Up @@ -118,6 +122,12 @@ const FilesAndUploads = ({
openDeleteConfirmation();
};

const handleOpenAssetInfo = (original) => {
setSelectedRows([{ original }]);
dispatch(getUsagePaths({ asset: original, courseId, setSelectedRows }));
openAssetInfo();
};

const headerActions = ({ selectedFlatRows }) => (
<TableActions
{...{
Expand All @@ -137,6 +147,7 @@ const FilesAndUploads = ({
{...{
handleLockedAsset,
handleOpenDeleteConfirmation,
handleOpenAssetInfo,
className,
original,
}}
Expand All @@ -148,6 +159,7 @@ const FilesAndUploads = ({
{...{
handleLockedAsset,
handleOpenDeleteConfirmation,
handleOpenAssetInfo,
className,
original,
}}
Expand All @@ -162,7 +174,6 @@ const FilesAndUploads = ({
</div>
);
}

return (
<FilesAndUploadsProvider courseId={courseId}>
<main className="containerpt-5">
Expand All @@ -171,19 +182,38 @@ const FilesAndUploads = ({
hideHeading={false}
isError={addAssetStatus === RequestStatus.FAILED}
>
{intl.formatMessage(messages.errorAlertMessage, { message: errorMessages.upload })}
<ul className="p-0">
{errorMessages.upload.map(message => (
<li style={{ listStyle: 'none' }}>
{intl.formatMessage(messages.errorAlertMessage, { message })}
</li>
))}
</ul>
</ErrorAlert>
<ErrorAlert
hideHeading={false}
isError={deleteAssetStatus === RequestStatus.FAILED}
>
{intl.formatMessage(messages.errorAlertMessage, { message: errorMessages.delete })}
<ul className="p-0">
{errorMessages.delete.map(message => (
<li style={{ listStyle: 'none' }}>
{intl.formatMessage(messages.errorAlertMessage, { message })}
</li>
))}
</ul>
</ErrorAlert>
<ErrorAlert
hideHeading={false}
isError={saveAssetStatus === RequestStatus.FAILED}
isError={updateAssetStatus === RequestStatus.FAILED}
>
{intl.formatMessage(messages.errorAlertMessage, { message: errorMessages.lock })}
<ul className="p-0">
{errorMessages.lock.map(message => (
<li style={{ listStyle: 'none' }}>
{intl.formatMessage(messages.errorAlertMessage, { message })}
</li>
))}
</ul>

</ErrorAlert>
<div className="h2">
<FormattedMessage {...messages.heading} />
Expand Down Expand Up @@ -236,28 +266,12 @@ const FilesAndUploads = ({
},
],
},
{
Header: 'Locked',
accessor: 'locked',
// Filter: CheckboxFilter,
// filter: 'exactText',
// filterChoices: [
// {
// name: 'Locked',
// value: true,
// },
// {
// name: 'Unlocked',
// value: false,
// },
// ],
},
]}
itemCount={totalCount}
pageCount={Math.ceil(totalCount / 50)}
data={assets}
>
{_.isEmpty(assets) && loadingStatus !== RequestStatus.IN_PROGRESS ? (
{isEmpty(assets) && loadingStatus !== RequestStatus.IN_PROGRESS ? (
<Dropzone
data-testid="files-dropzone"
onProcessUpload={handleDropzoneAsset}
Expand Down Expand Up @@ -292,7 +306,16 @@ const FilesAndUploads = ({
)}
</DataTable>
<FileInput fileInput={fileInputControl} />

{!isEmpty(selectedRows) && (
<FileInfo
asset={selectedRows[0].original}
onClose={closeAssetinfo}
isOpen={isAssetInfoOpen}
handleLockedAsset={handleLockedAsset}
usagePathStatus={usagePathStatus}
error={errorMessages.usageMetrics}
/>
)}
<AlertModal
title={intl.formatMessage(messages.deleteConfirmationTitle)}
isOpen={isDeleteConfirmationOpen}
Expand Down
Loading

0 comments on commit e50b8c7

Please sign in to comment.