diff --git a/frontend/src/App/AppRoutes.tsx b/frontend/src/App/AppRoutes.tsx index b86d35e27..4ba7e16b0 100644 --- a/frontend/src/App/AppRoutes.tsx +++ b/frontend/src/App/AppRoutes.tsx @@ -32,7 +32,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 ; @@ -88,7 +88,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 2783693a1..000000000 --- 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 MissingRowConnector from './MissingRowConnector'; - -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 episodeIds = this.getSelectedIds(); - - this.props.batchToggleMissingEpisodes({ - episodeIds, - 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, - isSearchingForMissingEpisodes, - 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('SearchForAllMissingEpisodesConfirmationCount', { 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, - isSearchingForMissingEpisodes: PropTypes.bool.isRequired, - isSaving: PropTypes.bool.isRequired, - onFilterSelect: PropTypes.func.isRequired, - onSearchSelectedPress: PropTypes.func.isRequired, - batchToggleMissingEpisodes: 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 000000000..b678c24d2 --- /dev/null +++ b/frontend/src/Wanted/Missing/Missing.tsx @@ -0,0 +1,379 @@ +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 { + batchToggleMissingEpisodes, + 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 isSearchingForAllEpisodes = useSelector( + createCommandExecutingSelector(commandNames.CUTOFF_UNMET_EPISODE_SEARCH) + ); + const isSearchingForSelectedEpisodes = useSelector( + createCommandExecutingSelector(commandNames.EPISODE_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 isSearchingForEpisodes = + isSearchingForAllEpisodes || isSearchingForSelectedEpisodes; + + 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.EPISODE_SEARCH, + episodeIds: selectedIds, + commandFinished: () => { + dispatch(fetchMissing()); + }, + }) + ); + }, [selectedIds, dispatch]); + + const handleSearchAllPress = useCallback(() => { + setIsConfirmSearchAllModalOpen(true); + }, []); + + const handleConfirmSearchAllMissingModalClose = useCallback(() => { + setIsConfirmSearchAllModalOpen(false); + }, []); + + const handleSearchAllMissingConfirmed = useCallback(() => { + dispatch( + executeCommand({ + name: commandNames.CUTOFF_UNMET_EPISODE_SEARCH, + commandFinished: () => { + dispatch(fetchMissing()); + }, + }) + ); + + setIsConfirmSearchAllModalOpen(false); + }, [dispatch]); + + const handleToggleSelectedPress = useCallback(() => { + dispatch( + batchToggleMissingEpisodes({ + episodeIds: 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); + + 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('SearchForMissingEpisodesConfirmationCount', { + 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 576e4362c..000000000 --- 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_EPISODE_SEARCH), - createCommandExecutingSelector(commandNames.EPISODE_SEARCH), - (missing, isSearchingForAllMissingEpisodes, isSearchingForSelectedMissingEpisodes) => { - return { - isSearchingForMissingEpisodes: isSearchingForAllMissingEpisodes || isSearchingForSelectedMissingEpisodes, - 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, ['episodeFileUpdated', 'episodeFileDeleted']); - - if (useCurrentPage) { - fetchMissing(); - } else { - gotoMissingFirstPage(); - } - } - - componentDidUpdate(prevProps) { - if (hasDifferentItems(prevProps.items, this.props.items)) { - const episodeIds = selectUniqueIds(this.props.items, 'id'); - this.props.fetchQueueDetails({ episodeIds }); - } - } - - 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.EPISODE_SEARCH, - episodeIds: selected, - commandFinished: this.repopulate - }); - }; - - onSearchAllMissingPress = (monitored) => { - this.props.executeCommand({ - name: commandNames.MISSING_EPISODE_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 b5d02db21..000000000 --- a/frontend/src/Wanted/Missing/MissingRow.js +++ /dev/null @@ -1,177 +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 episodeEntities from 'Episode/episodeEntities'; -import EpisodeSearchCell from 'Episode/EpisodeSearchCell'; -import EpisodeStatus from 'Episode/EpisodeStatus'; -import EpisodeTitleLink from 'Episode/EpisodeTitleLink'; -import SeasonEpisodeNumber from 'Episode/SeasonEpisodeNumber'; -import SeriesTitleLink from 'Series/SeriesTitleLink'; -import styles from './MissingRow.css'; - -function MissingRow(props) { - const { - id, - episodeFileId, - series, - seasonNumber, - episodeNumber, - absoluteEpisodeNumber, - sceneSeasonNumber, - sceneEpisodeNumber, - sceneAbsoluteEpisodeNumber, - unverifiedSceneNumbering, - airDateUtc, - lastSearchTime, - title, - isSelected, - columns, - onSelectedChange - } = props; - - if (!series) { - return null; - } - - return ( - - - - { - columns.map((column) => { - const { - name, - isVisible - } = column; - - if (!isVisible) { - return null; - } - - if (name === 'series.sortTitle') { - return ( - - - - ); - } - - if (name === 'episode') { - return ( - - - - ); - } - - if (name === 'episodes.title') { - return ( - - - - ); - } - - if (name === 'episodes.airDateUtc') { - return ( - - ); - } - - if (name === 'episodes.lastSearchTime') { - return ( - - ); - } - - if (name === 'status') { - return ( - - - - ); - } - - if (name === 'actions') { - return ( - - ); - } - - return null; - }) - } - - ); -} - -MissingRow.propTypes = { - id: PropTypes.number.isRequired, - episodeFileId: PropTypes.number, - series: PropTypes.object.isRequired, - seasonNumber: PropTypes.number.isRequired, - episodeNumber: PropTypes.number.isRequired, - absoluteEpisodeNumber: PropTypes.number, - sceneSeasonNumber: PropTypes.number, - sceneEpisodeNumber: PropTypes.number, - sceneAbsoluteEpisodeNumber: PropTypes.number, - unverifiedSceneNumbering: PropTypes.bool.isRequired, - airDateUtc: PropTypes.string.isRequired, - lastSearchTime: PropTypes.string, - title: PropTypes.string.isRequired, - 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 000000000..1d314524d --- /dev/null +++ b/frontend/src/Wanted/Missing/MissingRow.tsx @@ -0,0 +1,162 @@ +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 EpisodeSearchCell from 'Episode/EpisodeSearchCell'; +import EpisodeStatus from 'Episode/EpisodeStatus'; +import EpisodeTitleLink from 'Episode/EpisodeTitleLink'; +import SeasonEpisodeNumber from 'Episode/SeasonEpisodeNumber'; +import SeriesTitleLink from 'Series/SeriesTitleLink'; +import useSeries from 'Series/useSeries'; +import { SelectStateInputProps } from 'typings/props'; +import styles from './MissingRow.css'; + +interface MissingRowProps { + id: number; + seriesId: number; + episodeFileId?: number; + seasonNumber: number; + episodeNumber: number; + absoluteEpisodeNumber?: number; + sceneSeasonNumber?: number; + sceneEpisodeNumber?: number; + sceneAbsoluteEpisodeNumber?: number; + unverifiedSceneNumbering: boolean; + airDateUtc?: string; + lastSearchTime?: string; + title: string; + isSelected?: boolean; + columns: Column[]; + onSelectedChange: (options: SelectStateInputProps) => void; +} + +function MissingRow({ + id, + seriesId, + episodeFileId = 0, + seasonNumber, + episodeNumber, + absoluteEpisodeNumber, + sceneSeasonNumber, + sceneEpisodeNumber, + sceneAbsoluteEpisodeNumber, + unverifiedSceneNumbering, + airDateUtc, + lastSearchTime, + title, + isSelected, + columns, + onSelectedChange, +}: MissingRowProps) { + const series = useSeries(seriesId); + + if (!series) { + return null; + } + + return ( + + + + {columns.map((column) => { + const { name, isVisible } = column; + + if (!isVisible) { + return null; + } + + if (name === 'series.sortTitle') { + return ( + + + + ); + } + + if (name === 'episode') { + return ( + + + + ); + } + + if (name === 'episodes.title') { + return ( + + + + ); + } + + if (name === 'episodes.airDateUtc') { + return ; + } + + if (name === 'episodes.lastSearchTime') { + return ( + + ); + } + + if (name === 'status') { + return ( + + + + ); + } + + if (name === 'actions') { + return ( + + ); + } + + return null; + })} + + ); +} + +export default MissingRow; diff --git a/frontend/src/Wanted/Missing/MissingRowConnector.js b/frontend/src/Wanted/Missing/MissingRowConnector.js deleted file mode 100644 index f7eefca4d..000000000 --- a/frontend/src/Wanted/Missing/MissingRowConnector.js +++ /dev/null @@ -1,17 +0,0 @@ -import { connect } from 'react-redux'; -import { createSelector } from 'reselect'; -import createSeriesSelector from 'Store/Selectors/createSeriesSelector'; -import MissingRow from './MissingRow'; - -function createMapStateToProps() { - return createSelector( - createSeriesSelector(), - (series) => { - return { - series - }; - } - ); -} - -export default connect(createMapStateToProps)(MissingRow);