Skip to content

Commit

Permalink
feat: Add slots for video and file upload components and alerts
Browse files Browse the repository at this point in the history
This change add plugin slots for the file and video upload components, and the alerts components on those pages.
  • Loading branch information
xitij2000 committed Nov 20, 2024
1 parent 32e5fa6 commit 694edb1
Show file tree
Hide file tree
Showing 9 changed files with 568 additions and 523 deletions.
2 changes: 2 additions & 0 deletions src/course-outline/page-alerts/PageAlerts.jsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import CourseOutlinePageAlertsSlot from 'CourseAuthoring/plugin-slots/CourseOutlinePageAlertsSlot';
import React, { useState } from 'react';
import PropTypes from 'prop-types';
import { uniqBy } from 'lodash';
Expand Down Expand Up @@ -407,6 +408,7 @@ const PageAlerts = ({
{errorFilesPasteAlert()}
{conflictingFilesPasteAlert()}
{newFilesPasteAlert()}
<CourseOutlinePageAlertsSlot />
</>
);
};
Expand Down
193 changes: 17 additions & 176 deletions src/files-and-videos/files-page/FilesPage.jsx
Original file line number Diff line number Diff line change
@@ -1,174 +1,37 @@
import React, { useEffect } from 'react';
import { useIntl } from '@edx/frontend-platform/i18n';
import { Container } from '@openedx/paragon';
import CourseFilesSlot from 'CourseAuthoring/plugin-slots/CourseFilesSlot';
import PropTypes from 'prop-types';
import React, { useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { injectIntl, FormattedMessage, intlShape } from '@edx/frontend-platform/i18n';
import { CheckboxFilter, Container } from '@openedx/paragon';
import Placeholder from '../../editors/Placeholder';

import { RequestStatus } from '../../data/constants';
import { useModels, useModel } from '../../generic/model-store';
import {
addAssetFile,
deleteAssetFile,
fetchAssets,
updateAssetLock,
fetchAssetDownload,
getUsagePaths,
resetErrors,
updateAssetOrder,
validateAssetFiles,
} from './data/thunks';
import messages from './messages';
import FilesPageProvider from './FilesPageProvider';
import Placeholder from '../../editors/Placeholder';
import { useModel } from '../../generic/model-store';
import getPageHeadTitle from '../../generic/utils';
import {
AccessColumn,
ActiveColumn,
EditFileErrors,
FileTable,
ThumbnailColumn,
} from '../generic';
import { getFileSizeToClosestByte } from '../../utils';
import FileThumbnail from './FileThumbnail';
import FileInfoModalSidebar from './FileInfoModalSidebar';
import FileValidationModal from './FileValidationModal';
import { EditFileErrors } from '../generic';
import { fetchAssets, resetErrors } from './data/thunks';
import FilesPageProvider from './FilesPageProvider';
import messages from './messages';

const FilesPage = ({
courseId,
// injected
intl,
}) => {
const dispatch = useDispatch();
const intl = useIntl();
const courseDetails = useModel('courseDetails', courseId);
document.title = getPageHeadTitle(courseDetails?.name, intl.formatMessage(messages.heading));

useEffect(() => {
dispatch(fetchAssets(courseId));
}, [courseId]);

const {
assetIds,
loadingStatus,
addingStatus: addAssetStatus,
deletingStatus: deleteAssetStatus,
updatingStatus: updateAssetStatus,
usageStatus: usagePathStatus,
errors: errorMessages,
} = useSelector(state => state.assets);

useEffect(() => {
dispatch(fetchAssets(courseId));
}, [courseId]);
const handleErrorReset = (error) => dispatch(resetErrors(error));
const handleDeleteFile = (id) => dispatch(deleteAssetFile(courseId, id));
const handleDownloadFile = (selectedRows) => dispatch(fetchAssetDownload({ selectedRows, courseId }));
const handleAddFile = (files) => {
handleErrorReset({ errorType: 'add' });
dispatch(validateAssetFiles(courseId, files));
};
const handleFileOverwrite = (close, files) => {
Object.values(files).forEach(file => dispatch(addAssetFile(courseId, file, true)));
close();
};
const handleLockFile = (fileId, locked) => {
handleErrorReset({ errorType: 'lock' });
dispatch(updateAssetLock({ courseId, assetId: fileId, locked }));
};
const handleUsagePaths = (asset) => dispatch(getUsagePaths({ asset, courseId }));
const handleFileOrder = ({ newFileIdOrder, sortType }) => {
dispatch(updateAssetOrder(courseId, newFileIdOrder, sortType));
};

const thumbnailPreview = (props) => FileThumbnail(props);
const infoModalSidebar = (asset) => FileInfoModalSidebar({
asset,
handleLockedAsset: handleLockFile,
});

const assets = useModels('assets', assetIds);
const data = {
fileIds: assetIds,
loadingStatus,
usagePathStatus,
usageErrorMessages: errorMessages.usageMetrics,
fileType: 'file',
};
const maxFileSize = 20 * 1048576;

const activeColumn = {
id: 'activeStatus',
Header: 'Active',
accessor: 'activeStatus',
Cell: ({ row }) => ActiveColumn({ row, pageLoadStatus: loadingStatus }),
Filter: CheckboxFilter,
filter: 'exactTextCase',
filterChoices: [
{ name: intl.formatMessage(messages.activeCheckboxLabel), value: 'active' },
{ name: intl.formatMessage(messages.inactiveCheckboxLabel), value: 'inactive' },
],
};
const accessColumn = {
id: 'lockStatus',
Header: 'Access',
accessor: 'lockStatus',
Cell: ({ row }) => AccessColumn({ row }),
Filter: CheckboxFilter,
filterChoices: [
{ name: intl.formatMessage(messages.lockedCheckboxLabel), value: 'locked' },
{ name: intl.formatMessage(messages.publicCheckboxLabel), value: 'public' },
],
};
const thumbnailColumn = {
id: 'thumbnail',
Header: '',
Cell: ({ row }) => ThumbnailColumn({ row, thumbnailPreview }),
};
const fileSizeColumn = {
id: 'fileSize',
Header: 'File size',
accessor: 'fileSize',
Cell: ({ row }) => {
const { fileSize } = row.original;
return getFileSizeToClosestByte(fileSize);
},
};

const tableColumns = [
{ ...thumbnailColumn },
{
Header: 'File name',
accessor: 'displayName',
},
{ ...fileSizeColumn },
{
Header: 'Type',
accessor: 'wrapperType',
Filter: CheckboxFilter,
filter: 'includesValue',
filterChoices: [
{
name: intl.formatMessage(messages.codeCheckboxLabel),
value: 'code',
},
{
name: intl.formatMessage(messages.imageCheckboxLabel),
value: 'image',
},
{
name: intl.formatMessage(messages.documentCheckboxLabel),
value: 'document',
},
{
name: intl.formatMessage(messages.audioCheckboxLabel),
value: 'audio',
},
{
name: intl.formatMessage(messages.otherCheckboxLabel),
value: 'other',
},
],
},
{ ...activeColumn },
{ ...accessColumn },
];

if (loadingStatus === RequestStatus.DENIED) {
return (
<div data-testid="under-construction-placeholder" className="row justify-contnt-center m-6">
Expand All @@ -189,30 +52,10 @@ const FilesPage = ({
loadingStatus={loadingStatus}
/>
<div className="h2">
<FormattedMessage {...messages.heading} />
{intl.formatMessage(messages.heading)}
</div>
{loadingStatus !== RequestStatus.FAILED && (
<>
<FileTable
{...{
courseId,
data,
handleAddFile,
handleDeleteFile,
handleDownloadFile,
handleLockFile,
handleUsagePaths,
handleErrorReset,
handleFileOrder,
tableColumns,
maxFileSize,
thumbnailPreview,
infoModalSidebar,
files: assets,
}}
/>
<FileValidationModal {...{ handleFileOverwrite }} />
</>
<CourseFilesSlot courseId={courseId} />
)}
</Container>
</FilesPageProvider>
Expand All @@ -221,8 +64,6 @@ const FilesPage = ({

FilesPage.propTypes = {
courseId: PropTypes.string.isRequired,
// injected
intl: intlShape.isRequired,
};

export default injectIntl(FilesPage);
export default FilesPage;
Loading

0 comments on commit 694edb1

Please sign in to comment.