diff --git a/src/files-and-videos/files-page/FilesPage.jsx b/src/files-and-videos/files-page/FilesPage.jsx
index 3439b8cd3..318c5e333 100644
--- a/src/files-and-videos/files-page/FilesPage.jsx
+++ b/src/files-and-videos/files-page/FilesPage.jsx
@@ -1,5 +1,6 @@
import React, { useEffect } from 'react';
import PropTypes from 'prop-types';
+import { isEmpty } from 'lodash';
import { useDispatch, useSelector } from 'react-redux';
import { injectIntl, FormattedMessage, intlShape } from '@edx/frontend-platform/i18n';
import { CheckboxFilter } from '@edx/paragon';
@@ -85,12 +86,24 @@ const FilesPage = ({
const activeColumn = {
id: 'usageLocations',
Header: 'Active',
+ accessor: (({ usageLocations }) => !isEmpty(usageLocations)),
Cell: ({ row }) => ActiveColumn({ row }),
+ Filter: CheckboxFilter,
+ filterChoices: [
+ { name: intl.formatMessage(messages.activeCheckboxLabel), value: true },
+ { name: intl.formatMessage(messages.inactiveCheckboxLabel), value: false },
+ ],
};
const accessColumn = {
id: 'locked',
Header: 'Access',
+ accessor: 'locked',
Cell: ({ row }) => AccessColumn({ row }),
+ Filter: CheckboxFilter,
+ filterChoices: [
+ { name: intl.formatMessage(messages.lockedCheckboxLabel), value: true },
+ { name: intl.formatMessage(messages.publicCheckboxLabel), value: false },
+ ],
};
const thumbnailColumn = {
id: 'thumbnail',
@@ -100,6 +113,7 @@ const FilesPage = ({
const fileSizeColumn = {
id: 'fileSize',
Header: 'File size',
+ accessor: 'fileSize',
Cell: ({ row }) => {
const { fileSize } = row.original;
return getFileSizeToClosestByte(fileSize);
@@ -120,21 +134,25 @@ const FilesPage = ({
filter: 'includesValue',
filterChoices: [
{
- name: 'Code',
+ name: intl.formatMessage(messages.codeCheckboxLabel),
value: 'code',
},
{
- name: 'Images',
+ name: intl.formatMessage(messages.imageCheckboxLabel),
value: 'image',
},
{
- name: 'Documents',
+ name: intl.formatMessage(messages.documentCheckboxLabel),
value: 'document',
},
{
- name: 'Audio',
+ name: intl.formatMessage(messages.audioCheckboxLabel),
value: 'audio',
},
+ {
+ name: intl.formatMessage(messages.otherCheckboxLabel),
+ value: 'other',
+ },
],
},
{ ...activeColumn },
diff --git a/src/files-and-videos/files-page/messages.js b/src/files-and-videos/files-page/messages.js
index ccc1273dc..b06b48d45 100644
--- a/src/files-and-videos/files-page/messages.js
+++ b/src/files-and-videos/files-page/messages.js
@@ -45,6 +45,42 @@ const messages = defineMessages({
you lock a file, the web URL only allows learners who are enrolled
in your course and signed in to access the file.`,
},
+ activeCheckboxLabel: {
+ id: 'course-authoring.files-and-videos.sort-and-filter.modal.filter.activeCheckbox.label',
+ defaultMessage: 'Active',
+ },
+ inactiveCheckboxLabel: {
+ id: 'course-authoring.files-and-videos.sort-and-filter.modal.filter.inactiveCheckbox.label',
+ defaultMessage: 'Inactive',
+ },
+ lockedCheckboxLabel: {
+ id: 'course-authoring.files-and-videos.sort-and-filter.modal.filter.lockedCheckbox.label',
+ defaultMessage: 'Locked',
+ },
+ publicCheckboxLabel: {
+ id: 'course-authoring.files-and-videos.sort-and-filter.modal.filter.publicCheckbox.label',
+ defaultMessage: 'Public',
+ },
+ codeCheckboxLabel: {
+ id: 'course-authoring.files-and-videos.sort-and-filter.modal.filter.codeCheckbox.label',
+ defaultMessage: 'Code',
+ },
+ imageCheckboxLabel: {
+ id: 'course-authoring.files-and-videos.sort-and-filter.modal.filter.imageCheckbox.label',
+ defaultMessage: 'Images',
+ },
+ documentCheckboxLabel: {
+ id: 'course-authoring.files-and-videos.sort-and-filter.modal.filter.documentCheckbox.label',
+ defaultMessage: 'Documents',
+ },
+ audioCheckboxLabel: {
+ id: 'course-authoring.files-and-videos.sort-and-filter.modal.filter.audioCheckbox.label',
+ defaultMessage: 'Audio',
+ },
+ otherCheckboxLabel: {
+ id: 'course-authoring.files-and-videos.sort-and-filter.modal.filter.otherCheckbox.label',
+ defaultMessage: 'Other',
+ },
});
export default messages;
diff --git a/src/files-and-videos/generic/FileTable.jsx b/src/files-and-videos/generic/FileTable.jsx
index 065df94f6..9ad2a0087 100644
--- a/src/files-and-videos/generic/FileTable.jsx
+++ b/src/files-and-videos/generic/FileTable.jsx
@@ -22,10 +22,11 @@ import FileInput, { useFileInput } from './FileInput';
import {
GalleryCard,
TableActions,
+ RowStatus,
+ MoreInfoColumn,
+ FilterStatus,
} from './table-components';
import ApiStatusToast from './ApiStatusToast';
-import FilterStatus from './table-components/FilterStatus';
-import MoreInfoColumn from './table-components/table-custom-columns/MoreInfoColumn';
const FileTable = ({
files,
@@ -171,7 +172,7 @@ const FileTable = ({
}
return (
- <>
+
{isEmpty(files) && loadingStatus !== RequestStatus.IN_PROGRESS ? (
{intl.formatMessage(messages.deleteConfirmationMessage, { fileNumber: selectedRows.length })}
- >
+
);
};
diff --git a/src/files-and-videos/generic/messages.js b/src/files-and-videos/generic/messages.js
index 883bcaa7b..c7340d3bc 100644
--- a/src/files-and-videos/generic/messages.js
+++ b/src/files-and-videos/generic/messages.js
@@ -1,6 +1,10 @@
import { defineMessages } from '@edx/frontend-platform/i18n';
const messages = defineMessages({
+ rowStatusMessage: {
+ id: 'course-authoring.files-and-upload.rowStatus.message',
+ defaultMessage: 'Showing {fileCount} of {rowCount}',
+ },
apiStatusToastMessage: {
id: 'course-authoring.files-and-upload.apiStatus.message',
defaultMessage: '{actionType} {selectedRowCount} file(s)',
@@ -95,7 +99,7 @@ const messages = defineMessages({
},
sortButtonLabel: {
id: 'course-authoring.files-and-uploads.sortButton.label',
- defaultMessage: 'Sort',
+ defaultMessage: 'Sort and Filter',
},
sortModalTitleLabel: {
id: 'course-authoring.files-and-uploads.sortModal.title',
diff --git a/src/files-and-videos/generic/table-components/FilterStatus.jsx b/src/files-and-videos/generic/table-components/FilterStatus.jsx
index 88fe561db..9c8f5e7c5 100644
--- a/src/files-and-videos/generic/table-components/FilterStatus.jsx
+++ b/src/files-and-videos/generic/table-components/FilterStatus.jsx
@@ -1,46 +1,56 @@
import React, { useContext } from 'react';
import PropTypes from 'prop-types';
import { FormattedMessage } from '@edx/frontend-platform/i18n';
-import { DataTableContext, Button } from '@edx/paragon';
+import {
+ DataTableContext, Button, Row, Chip,
+} from '@edx/paragon';
+import { Close } from '@edx/paragon/icons';
+import { getFilters, removeFilter } from './utils';
const FilterStatus = ({
className, variant, size, clearFiltersText, buttonClassName,
}) => {
const {
- setAllFilters, RowStatusComponent, page, rows,
+ state, setAllFilters, setFilter, RowStatusComponent, columns,
} = useContext(DataTableContext);
+
if (!setAllFilters) {
return null;
}
- const RowStatus = RowStatusComponent;
-
- const pageSize = page?.length || rows?.length;
+ const filters = getFilters(state, columns);
return (
-
- Filters applied
- {!!pageSize && ' ('}
-
- {!!pageSize && ')'}
-
-
+
+
+ Filters applied
+ {filters.map(({ name, value }) => (
+ removeFilter(value, setFilter, setAllFilters, state)}
+ >
+ {name}
+
+ ))}
+
+
);
};
diff --git a/src/files-and-videos/generic/table-components/RowStatus.jsx b/src/files-and-videos/generic/table-components/RowStatus.jsx
new file mode 100644
index 000000000..0733e8757
--- /dev/null
+++ b/src/files-and-videos/generic/table-components/RowStatus.jsx
@@ -0,0 +1,30 @@
+import React, { useContext } from 'react';
+import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
+import { DataTableContext } from '@edx/paragon';
+import { getCurrentViewRange } from './utils';
+
+const RowStatus = ({
+ // injected
+ intl,
+}) => {
+ const { filteredRows, page, initialRows } = useContext(DataTableContext);
+
+ return (
+
+
+ {getCurrentViewRange({
+ filterRowCount: filteredRows.length,
+ initialRowCount: initialRows.length,
+ fileCount: page.length,
+ intl,
+ })}
+
+
+ );
+};
+
+RowStatus.propTypes = {
+ intl: intlShape.isRequired,
+};
+
+export default injectIntl(RowStatus);
diff --git a/src/files-and-videos/generic/table-components/TableActions.jsx b/src/files-and-videos/generic/table-components/TableActions.jsx
index af8cb49df..d36f0606c 100644
--- a/src/files-and-videos/generic/table-components/TableActions.jsx
+++ b/src/files-and-videos/generic/table-components/TableActions.jsx
@@ -1,18 +1,16 @@
-import React, { useState } from 'react';
+import React from 'react';
import _ from 'lodash';
import { PropTypes } from 'prop-types';
-import { injectIntl, FormattedMessage, intlShape } from '@edx/frontend-platform/i18n';
+import { injectIntl, FormattedMessage } from '@edx/frontend-platform/i18n';
import { getConfig } from '@edx/frontend-platform';
import {
- ActionRow,
Button,
Dropdown,
- ModalDialog,
- SelectableBox,
useToggle,
} from '@edx/paragon';
-import { Add } from '@edx/paragon/icons';
+import { Add, Tune } from '@edx/paragon/icons';
import messages from '../messages';
+import SortAndFilterModal from './sort-and-filter-modal';
const TableActions = ({
selectedFlatRows,
@@ -21,17 +19,11 @@ const TableActions = ({
handleBulkDownload,
handleOpenDeleteConfirmation,
encodingsDownloadUrl,
- // injected
- intl,
}) => {
const [isSortOpen, openSort, closeSort] = useToggle(false);
- const [sortBy, setSortBy] = useState('dateAdded,desc');
- const handleChange = (e) => {
- setSortBy(e.target.value);
- };
return (
<>
-