diff --git a/frontend/src/App/AppRoutes.tsx b/frontend/src/App/AppRoutes.tsx index 81dadc8d01..bac4d40df4 100644 --- a/frontend/src/App/AppRoutes.tsx +++ b/frontend/src/App/AppRoutes.tsx @@ -33,7 +33,7 @@ import Tasks from 'System/Tasks/Tasks'; import Updates from 'System/Updates/Updates'; import getPathWithUrlBase from 'Utilities/getPathWithUrlBase'; import CutoffUnmet from 'Wanted/CutoffUnmet/CutoffUnmet'; -import MissingConnector from 'Wanted/Missing/MissingConnector'; +import Missing from 'Wanted/Missing/Missing'; function RedirectWithUrlBase() { return ; @@ -89,7 +89,7 @@ function AppRoutes() { Wanted */} - + diff --git a/frontend/src/Wanted/Missing/Missing.js b/frontend/src/Wanted/Missing/Missing.js deleted file mode 100644 index da243fb047..0000000000 --- a/frontend/src/Wanted/Missing/Missing.js +++ /dev/null @@ -1,312 +0,0 @@ -import PropTypes from 'prop-types'; -import React, { Component } from 'react'; -import Alert from 'Components/Alert'; -import LoadingIndicator from 'Components/Loading/LoadingIndicator'; -import FilterMenu from 'Components/Menu/FilterMenu'; -import ConfirmModal from 'Components/Modal/ConfirmModal'; -import PageContent from 'Components/Page/PageContent'; -import PageContentBody from 'Components/Page/PageContentBody'; -import PageToolbar from 'Components/Page/Toolbar/PageToolbar'; -import PageToolbarButton from 'Components/Page/Toolbar/PageToolbarButton'; -import PageToolbarSection from 'Components/Page/Toolbar/PageToolbarSection'; -import PageToolbarSeparator from 'Components/Page/Toolbar/PageToolbarSeparator'; -import Table from 'Components/Table/Table'; -import TableBody from 'Components/Table/TableBody'; -import TableOptionsModalWrapper from 'Components/Table/TableOptions/TableOptionsModalWrapper'; -import TablePager from 'Components/Table/TablePager'; -import { align, icons, kinds } from 'Helpers/Props'; -import InteractiveImportModal from 'InteractiveImport/InteractiveImportModal'; -import getFilterValue from 'Utilities/Filter/getFilterValue'; -import hasDifferentItems from 'Utilities/Object/hasDifferentItems'; -import translate from 'Utilities/String/translate'; -import getSelectedIds from 'Utilities/Table/getSelectedIds'; -import removeOldSelectedState from 'Utilities/Table/removeOldSelectedState'; -import selectAll from 'Utilities/Table/selectAll'; -import toggleSelected from 'Utilities/Table/toggleSelected'; -import MissingRow from './MissingRow'; - -function getMonitoredValue(props) { - const { - filters, - selectedFilterKey - } = props; - - return getFilterValue(filters, selectedFilterKey, 'monitored', false); -} - -class Missing extends Component { - - // - // Lifecycle - - constructor(props, context) { - super(props, context); - - this.state = { - allSelected: false, - allUnselected: false, - lastToggled: null, - selectedState: {}, - isConfirmSearchAllMissingModalOpen: false, - isInteractiveImportModalOpen: false - }; - } - - componentDidUpdate(prevProps) { - if (hasDifferentItems(prevProps.items, this.props.items)) { - this.setState((state) => { - return removeOldSelectedState(state, prevProps.items); - }); - } - } - - // - // Control - - getSelectedIds = () => { - return getSelectedIds(this.state.selectedState); - }; - - // - // Listeners - - onSelectAllChange = ({ value }) => { - this.setState(selectAll(this.state.selectedState, value)); - }; - - onSelectedChange = ({ id, value, shiftKey = false }) => { - this.setState((state) => { - return toggleSelected(state, this.props.items, id, value, shiftKey); - }); - }; - - onSearchSelectedPress = () => { - const selected = this.getSelectedIds(); - - this.props.onSearchSelectedPress(selected); - }; - - onToggleSelectedPress = () => { - const movieIds = this.getSelectedIds(); - - this.props.batchToggleMissingMovies({ - movieIds, - monitored: !getMonitoredValue(this.props) - }); - }; - - onSearchAllMissingPress = () => { - this.setState({ isConfirmSearchAllMissingModalOpen: true }); - }; - - onSearchAllMissingConfirmed = () => { - const { - selectedFilterKey, - onSearchAllMissingPress - } = this.props; - - // TODO: Custom filters will need to check whether there is a monitored - // filter once implemented. - - onSearchAllMissingPress(selectedFilterKey === 'monitored'); - this.setState({ isConfirmSearchAllMissingModalOpen: false }); - }; - - onConfirmSearchAllMissingModalClose = () => { - this.setState({ isConfirmSearchAllMissingModalOpen: false }); - }; - - onInteractiveImportPress = () => { - this.setState({ isInteractiveImportModalOpen: true }); - }; - - onInteractiveImportModalClose = () => { - this.setState({ isInteractiveImportModalOpen: false }); - }; - - // - // Render - - render() { - const { - isFetching, - isPopulated, - error, - items, - selectedFilterKey, - filters, - columns, - totalRecords, - isSearchingForMissingMovies, - isSaving, - onFilterSelect, - ...otherProps - } = this.props; - - const { - allSelected, - allUnselected, - selectedState, - isConfirmSearchAllMissingModalOpen, - isInteractiveImportModalOpen - } = this.state; - - const itemsSelected = !!this.getSelectedIds().length; - const isShowingMonitored = getMonitoredValue(this.props); - - return ( - - - - - - - - - - - - - - - - - - - - - - - - - - { - isFetching && !isPopulated && - - } - - { - !isFetching && error && - - {translate('MissingLoadError')} - - } - - { - isPopulated && !error && !items.length && - - {translate('MissingNoItems')} - - } - - { - isPopulated && !error && !!items.length && -
- - - { - items.map((item) => { - return ( - - ); - }) - } - -
- - - - -
- {translate('SearchForAllMissingMoviesConfirmationCount', { totalRecords })} -
-
- {translate('MassSearchCancelWarning')} -
-
- } - confirmLabel={translate('Search')} - onConfirm={this.onSearchAllMissingConfirmed} - onCancel={this.onConfirmSearchAllMissingModalClose} - /> - - } - - -
-
- ); - } -} - -Missing.propTypes = { - isFetching: PropTypes.bool.isRequired, - isPopulated: PropTypes.bool.isRequired, - error: PropTypes.object, - items: PropTypes.arrayOf(PropTypes.object).isRequired, - selectedFilterKey: PropTypes.string.isRequired, - filters: PropTypes.arrayOf(PropTypes.object).isRequired, - columns: PropTypes.arrayOf(PropTypes.object).isRequired, - totalRecords: PropTypes.number, - isSearchingForMissingMovies: PropTypes.bool.isRequired, - isSaving: PropTypes.bool.isRequired, - onFilterSelect: PropTypes.func.isRequired, - onSearchSelectedPress: PropTypes.func.isRequired, - batchToggleMissingMovies: PropTypes.func.isRequired, - onSearchAllMissingPress: PropTypes.func.isRequired -}; - -export default Missing; diff --git a/frontend/src/Wanted/Missing/Missing.tsx b/frontend/src/Wanted/Missing/Missing.tsx new file mode 100644 index 0000000000..75f7a77217 --- /dev/null +++ b/frontend/src/Wanted/Missing/Missing.tsx @@ -0,0 +1,383 @@ +import React, { useCallback, useEffect, useMemo, useState } from 'react'; +import { useDispatch, useSelector } from 'react-redux'; +import AppState, { Filter } from 'App/State/AppState'; +import * as commandNames from 'Commands/commandNames'; +import Alert from 'Components/Alert'; +import LoadingIndicator from 'Components/Loading/LoadingIndicator'; +import FilterMenu from 'Components/Menu/FilterMenu'; +import ConfirmModal from 'Components/Modal/ConfirmModal'; +import PageContent from 'Components/Page/PageContent'; +import PageContentBody from 'Components/Page/PageContentBody'; +import PageToolbar from 'Components/Page/Toolbar/PageToolbar'; +import PageToolbarButton from 'Components/Page/Toolbar/PageToolbarButton'; +import PageToolbarSection from 'Components/Page/Toolbar/PageToolbarSection'; +import PageToolbarSeparator from 'Components/Page/Toolbar/PageToolbarSeparator'; +import Table from 'Components/Table/Table'; +import TableBody from 'Components/Table/TableBody'; +import TableOptionsModalWrapper from 'Components/Table/TableOptions/TableOptionsModalWrapper'; +import TablePager from 'Components/Table/TablePager'; +import usePaging from 'Components/Table/usePaging'; +import useCurrentPage from 'Helpers/Hooks/useCurrentPage'; +import useSelectState from 'Helpers/Hooks/useSelectState'; +import { align, icons, kinds } from 'Helpers/Props'; +import InteractiveImportModal from 'InteractiveImport/InteractiveImportModal'; +import { executeCommand } from 'Store/Actions/commandActions'; +import { + batchToggleMissingMovies, + clearMissing, + fetchMissing, + gotoMissingPage, + setMissingFilter, + setMissingSort, + setMissingTableOption, +} from 'Store/Actions/wantedActions'; +import createCommandExecutingSelector from 'Store/Selectors/createCommandExecutingSelector'; +import { CheckInputChanged } from 'typings/inputs'; +import { SelectStateInputProps } from 'typings/props'; +import { TableOptionsChangePayload } from 'typings/Table'; +import getFilterValue from 'Utilities/Filter/getFilterValue'; +import { + registerPagePopulator, + unregisterPagePopulator, +} from 'Utilities/pagePopulator'; +import translate from 'Utilities/String/translate'; +import getSelectedIds from 'Utilities/Table/getSelectedIds'; +import MissingRow from './MissingRow'; + +function getMonitoredValue( + filters: Filter[], + selectedFilterKey: string +): boolean { + return !!getFilterValue(filters, selectedFilterKey, 'monitored', false); +} + +function Missing() { + const dispatch = useDispatch(); + const requestCurrentPage = useCurrentPage(); + + const { + isFetching, + isPopulated, + error, + items, + columns, + selectedFilterKey, + filters, + sortKey, + sortDirection, + page, + pageSize, + totalPages, + totalRecords = 0, + } = useSelector((state: AppState) => state.wanted.missing); + + const isSearchingForAllMovies = useSelector( + createCommandExecutingSelector(commandNames.MISSING_MOVIES_SEARCH) + ); + const isSearchingForSelectedMovies = useSelector( + createCommandExecutingSelector(commandNames.MOVIE_SEARCH) + ); + + const [selectState, setSelectState] = useSelectState(); + const { allSelected, allUnselected, selectedState } = selectState; + + const [isConfirmSearchAllModalOpen, setIsConfirmSearchAllModalOpen] = + useState(false); + + const [isInteractiveImportModalOpen, setIsInteractiveImportModalOpen] = + useState(false); + + const { + handleFirstPagePress, + handlePreviousPagePress, + handleNextPagePress, + handleLastPagePress, + handlePageSelect, + } = usePaging({ + page, + totalPages, + gotoPage: gotoMissingPage, + }); + + const selectedIds = useMemo(() => { + return getSelectedIds(selectedState); + }, [selectedState]); + + const isSaving = useMemo(() => { + return items.filter((m) => m.isSaving).length > 1; + }, [items]); + + const itemsSelected = !!selectedIds.length; + const isShowingMonitored = getMonitoredValue(filters, selectedFilterKey); + const isSearchingForMovies = + isSearchingForAllMovies || isSearchingForSelectedMovies; + + const handleSelectAllChange = useCallback( + ({ value }: CheckInputChanged) => { + setSelectState({ type: value ? 'selectAll' : 'unselectAll', items }); + }, + [items, setSelectState] + ); + + const handleSelectedChange = useCallback( + ({ id, value, shiftKey = false }: SelectStateInputProps) => { + setSelectState({ + type: 'toggleSelected', + items, + id, + isSelected: value, + shiftKey, + }); + }, + [items, setSelectState] + ); + + const handleSearchSelectedPress = useCallback(() => { + dispatch( + executeCommand({ + name: commandNames.MOVIE_SEARCH, + movieIds: selectedIds, + commandFinished: () => { + dispatch(fetchMissing()); + }, + }) + ); + }, [selectedIds, dispatch]); + + const handleSearchAllPress = useCallback(() => { + setIsConfirmSearchAllModalOpen(true); + }, []); + + const handleConfirmSearchAllMissingModalClose = useCallback(() => { + setIsConfirmSearchAllModalOpen(false); + }, []); + + const handleSearchAllMissingConfirmed = useCallback(() => { + dispatch( + executeCommand({ + name: commandNames.MISSING_MOVIES_SEARCH, + commandFinished: () => { + dispatch(fetchMissing()); + }, + }) + ); + + setIsConfirmSearchAllModalOpen(false); + }, [dispatch]); + + const handleToggleSelectedPress = useCallback(() => { + dispatch( + batchToggleMissingMovies({ + movieIds: selectedIds, + monitored: !isShowingMonitored, + }) + ); + }, [isShowingMonitored, selectedIds, dispatch]); + + const handleInteractiveImportPress = useCallback(() => { + setIsInteractiveImportModalOpen(true); + }, []); + + const handleInteractiveImportModalClose = useCallback(() => { + setIsInteractiveImportModalOpen(false); + }, []); + + const handleFilterSelect = useCallback( + (filterKey: number | string) => { + dispatch(setMissingFilter({ selectedFilterKey: filterKey })); + }, + [dispatch] + ); + + const handleSortPress = useCallback( + (sortKey: string) => { + dispatch(setMissingSort({ sortKey })); + }, + [dispatch] + ); + + const handleTableOptionChange = useCallback( + (payload: TableOptionsChangePayload) => { + dispatch(setMissingTableOption(payload)); + + if (payload.pageSize) { + dispatch(gotoMissingPage({ page: 1 })); + } + }, + [dispatch] + ); + + useEffect(() => { + if (requestCurrentPage) { + dispatch(fetchMissing()); + } else { + dispatch(gotoMissingPage({ page: 1 })); + } + + return () => { + dispatch(clearMissing()); + }; + }, [requestCurrentPage, dispatch]); + + useEffect(() => { + const repopulate = () => { + dispatch(fetchMissing()); + }; + + registerPagePopulator(repopulate, [ + 'movieUpdated', + 'movieFileUpdated', + 'movieFileDeleted', + ]); + + return () => { + unregisterPagePopulator(repopulate); + }; + }, [dispatch]); + + return ( + + + + + + + + + + + + + + + + + + + + + + + + + {isFetching && !isPopulated ? : null} + + {!isFetching && error ? ( + {translate('MissingLoadError')} + ) : null} + + {isPopulated && !error && !items.length ? ( + {translate('MissingNoItems')} + ) : null} + + {isPopulated && !error && !!items.length ? ( +
+ + + {items.map((item) => { + return ( + + ); + })} + +
+ + + + +
+ {translate('SearchForAllMissingMoviesConfirmationCount', { + totalRecords, + })} +
+
{translate('MassSearchCancelWarning')}
+
+ } + confirmLabel={translate('Search')} + onConfirm={handleSearchAllMissingConfirmed} + onCancel={handleConfirmSearchAllMissingModalClose} + /> + + ) : null} +
+ + +
+ ); +} + +export default Missing; diff --git a/frontend/src/Wanted/Missing/MissingConnector.js b/frontend/src/Wanted/Missing/MissingConnector.js deleted file mode 100644 index b09b3201a2..0000000000 --- a/frontend/src/Wanted/Missing/MissingConnector.js +++ /dev/null @@ -1,176 +0,0 @@ -import PropTypes from 'prop-types'; -import React, { Component } from 'react'; -import { connect } from 'react-redux'; -import { createSelector } from 'reselect'; -import * as commandNames from 'Commands/commandNames'; -import withCurrentPage from 'Components/withCurrentPage'; -import { executeCommand } from 'Store/Actions/commandActions'; -import { clearQueueDetails, fetchQueueDetails } from 'Store/Actions/queueActions'; -import * as wantedActions from 'Store/Actions/wantedActions'; -import createCommandExecutingSelector from 'Store/Selectors/createCommandExecutingSelector'; -import hasDifferentItems from 'Utilities/Object/hasDifferentItems'; -import selectUniqueIds from 'Utilities/Object/selectUniqueIds'; -import { registerPagePopulator, unregisterPagePopulator } from 'Utilities/pagePopulator'; -import Missing from './Missing'; - -function createMapStateToProps() { - return createSelector( - (state) => state.wanted.missing, - createCommandExecutingSelector(commandNames.MISSING_MOVIES_SEARCH), - createCommandExecutingSelector(commandNames.MOVIE_SEARCH), - (missing, isSearchingForMissingMovies, isSearchingForSelectedMissingMovies) => { - return { - isSearchingForMissingMovies: isSearchingForMissingMovies || isSearchingForSelectedMissingMovies, - isSaving: missing.items.filter((m) => m.isSaving).length > 1, - ...missing - }; - } - ); -} - -const mapDispatchToProps = { - ...wantedActions, - executeCommand, - fetchQueueDetails, - clearQueueDetails -}; - -class MissingConnector extends Component { - - // - // Lifecycle - - componentDidMount() { - const { - useCurrentPage, - fetchMissing, - gotoMissingFirstPage - } = this.props; - - registerPagePopulator(this.repopulate, ['movieFileUpdated', 'movieFileDeleted']); - - if (useCurrentPage) { - fetchMissing(); - } else { - gotoMissingFirstPage(); - } - } - - componentDidUpdate(prevProps) { - if (hasDifferentItems(prevProps.items, this.props.items)) { - const movieIds = selectUniqueIds(this.props.items, 'id'); - this.props.fetchQueueDetails({ movieIds }); - } - } - - componentWillUnmount() { - unregisterPagePopulator(this.repopulate); - this.props.clearMissing(); - this.props.clearQueueDetails(); - } - - // - // Control - - repopulate = () => { - this.props.fetchMissing(); - }; - - // - // Listeners - - onFirstPagePress = () => { - this.props.gotoMissingFirstPage(); - }; - - onPreviousPagePress = () => { - this.props.gotoMissingPreviousPage(); - }; - - onNextPagePress = () => { - this.props.gotoMissingNextPage(); - }; - - onLastPagePress = () => { - this.props.gotoMissingLastPage(); - }; - - onPageSelect = (page) => { - this.props.gotoMissingPage({ page }); - }; - - onSortPress = (sortKey) => { - this.props.setMissingSort({ sortKey }); - }; - - onFilterSelect = (selectedFilterKey) => { - this.props.setMissingFilter({ selectedFilterKey }); - }; - - onTableOptionChange = (payload) => { - this.props.setMissingTableOption(payload); - - if (payload.pageSize) { - this.props.gotoMissingFirstPage(); - } - }; - - onSearchSelectedPress = (selected) => { - this.props.executeCommand({ - name: commandNames.MOVIE_SEARCH, - movieIds: selected, - commandFinished: this.repopulate - }); - }; - - onSearchAllMissingPress = (monitored) => { - this.props.executeCommand({ - name: commandNames.MISSING_MOVIES_SEARCH, - monitored, - commandFinished: this.repopulate - }); - }; - - // - // Render - - render() { - return ( - - ); - } -} - -MissingConnector.propTypes = { - useCurrentPage: PropTypes.bool.isRequired, - items: PropTypes.arrayOf(PropTypes.object).isRequired, - fetchMissing: PropTypes.func.isRequired, - gotoMissingFirstPage: PropTypes.func.isRequired, - gotoMissingPreviousPage: PropTypes.func.isRequired, - gotoMissingNextPage: PropTypes.func.isRequired, - gotoMissingLastPage: PropTypes.func.isRequired, - gotoMissingPage: PropTypes.func.isRequired, - setMissingSort: PropTypes.func.isRequired, - setMissingFilter: PropTypes.func.isRequired, - setMissingTableOption: PropTypes.func.isRequired, - clearMissing: PropTypes.func.isRequired, - executeCommand: PropTypes.func.isRequired, - fetchQueueDetails: PropTypes.func.isRequired, - clearQueueDetails: PropTypes.func.isRequired -}; - -export default withCurrentPage( - connect(createMapStateToProps, mapDispatchToProps)(MissingConnector) -); diff --git a/frontend/src/Wanted/Missing/MissingRow.js b/frontend/src/Wanted/Missing/MissingRow.js deleted file mode 100644 index ffdd7161f0..0000000000 --- a/frontend/src/Wanted/Missing/MissingRow.js +++ /dev/null @@ -1,161 +0,0 @@ -import PropTypes from 'prop-types'; -import React from 'react'; -import RelativeDateCell from 'Components/Table/Cells/RelativeDateCell'; -import TableRowCell from 'Components/Table/Cells/TableRowCell'; -import TableSelectCell from 'Components/Table/Cells/TableSelectCell'; -import TableRow from 'Components/Table/TableRow'; -import movieEntities from 'Movie/movieEntities'; -import MovieSearchCell from 'Movie/MovieSearchCell'; -import MovieStatus from 'Movie/MovieStatus'; -import MovieTitleLink from 'Movie/MovieTitleLink'; -import styles from './MissingRow.css'; - -function MissingRow(props) { - const { - id, - movieFileId, - year, - title, - titleSlug, - inCinemas, - digitalRelease, - physicalRelease, - lastSearchTime, - isSelected, - columns, - onSelectedChange - } = props; - - if (!title) { - return null; - } - - return ( - - - - { - columns.map((column) => { - const { - name, - isVisible - } = column; - - if (!isVisible) { - return null; - } - - if (name === 'movieMetadata.sortTitle') { - return ( - - - - ); - } - - if (name === 'movieMetadata.year') { - return ( - - {year} - - ); - } - - if (name === 'movieMetadata.inCinemas') { - return ( - - ); - } - - if (name === 'movieMetadata.digitalRelease') { - return ( - - ); - } - - if (name === 'movieMetadata.physicalRelease') { - return ( - - ); - } - - if (name === 'movies.lastSearchTime') { - return ( - - ); - } - - if (name === 'status') { - return ( - - - - ); - } - - if (name === 'actions') { - return ( - - ); - } - - return null; - }) - } - - ); -} - -MissingRow.propTypes = { - id: PropTypes.number.isRequired, - movieFileId: PropTypes.number, - title: PropTypes.string.isRequired, - year: PropTypes.number.isRequired, - lastSearchTime: PropTypes.string, - titleSlug: PropTypes.string.isRequired, - inCinemas: PropTypes.string, - digitalRelease: PropTypes.string, - physicalRelease: PropTypes.string, - isSelected: PropTypes.bool, - columns: PropTypes.arrayOf(PropTypes.object).isRequired, - onSelectedChange: PropTypes.func.isRequired -}; - -export default MissingRow; diff --git a/frontend/src/Wanted/Missing/MissingRow.tsx b/frontend/src/Wanted/Missing/MissingRow.tsx new file mode 100644 index 0000000000..af14e56922 --- /dev/null +++ b/frontend/src/Wanted/Missing/MissingRow.tsx @@ -0,0 +1,141 @@ +import React from 'react'; +import RelativeDateCell from 'Components/Table/Cells/RelativeDateCell'; +import TableRowCell from 'Components/Table/Cells/TableRowCell'; +import TableSelectCell from 'Components/Table/Cells/TableSelectCell'; +import Column from 'Components/Table/Column'; +import TableRow from 'Components/Table/TableRow'; +import MovieSearchCell from 'Movie/MovieSearchCell'; +import MovieStatus from 'Movie/MovieStatus'; +import MovieTitleLink from 'Movie/MovieTitleLink'; +import { SelectStateInputProps } from 'typings/props'; +import styles from './MissingRow.css'; + +interface MissingRowProps { + id: number; + movieFileId?: number; + inCinemas?: string; + digitalRelease?: string; + physicalRelease?: string; + lastSearchTime?: string; + title: string; + year: number; + titleSlug: string; + isSelected?: boolean; + columns: Column[]; + onSelectedChange: (options: SelectStateInputProps) => void; +} + +function MissingRow({ + id, + movieFileId, + inCinemas, + digitalRelease, + physicalRelease, + lastSearchTime, + title, + year, + titleSlug, + isSelected, + columns, + onSelectedChange, +}: MissingRowProps) { + if (!title) { + return null; + } + + return ( + + + + {columns.map((column) => { + const { name, isVisible } = column; + + if (!isVisible) { + return null; + } + + if (name === 'movieMetadata.sortTitle') { + return ( + + + + ); + } + + if (name === 'movieMetadata.year') { + return {year}; + } + + if (name === 'movieMetadata.inCinemas') { + return ( + + ); + } + + if (name === 'movieMetadata.digitalRelease') { + return ( + + ); + } + + if (name === 'movieMetadata.physicalRelease') { + return ( + + ); + } + + if (name === 'movies.lastSearchTime') { + return ( + + ); + } + + if (name === 'status') { + return ( + + + + ); + } + + if (name === 'actions') { + return ( + + ); + } + + return null; + })} + + ); +} + +export default MissingRow;