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 Dec 19, 2024
1 parent b110b6b commit 9c79a41
Show file tree
Hide file tree
Showing 9 changed files with 569 additions and 521 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
192 changes: 18 additions & 174 deletions src/files-and-videos/files-page/FilesPage.jsx
Original file line number Diff line number Diff line change
@@ -1,173 +1,39 @@
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);

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);
},
};
useEffect(() => {
dispatch(fetchAssets(courseId));
}, [courseId]);

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 },
];
const handleErrorReset = (error) => dispatch(resetErrors(error));

if (loadingStatus === RequestStatus.DENIED) {
return (
Expand All @@ -189,30 +55,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 +67,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 9c79a41

Please sign in to comment.