From cd21865c4d5be00c13c372e0b7a058f61ec855a2 Mon Sep 17 00:00:00 2001 From: sct Date: Sat, 6 Mar 2021 00:46:53 +0900 Subject: [PATCH] feat(ui): request list redesign (#1099) --- .../RequestList/RequestItem/index.tsx | 438 +++++++++--------- src/components/RequestList/index.tsx | 227 ++++----- src/i18n/locale/en.json | 8 +- 3 files changed, 343 insertions(+), 330 deletions(-) diff --git a/src/components/RequestList/RequestItem/index.tsx b/src/components/RequestList/RequestItem/index.tsx index 73c60e98..16a98dd3 100644 --- a/src/components/RequestList/RequestItem/index.tsx +++ b/src/components/RequestList/RequestItem/index.tsx @@ -1,12 +1,7 @@ import React, { useContext, useState } from 'react'; import { useInView } from 'react-intersection-observer'; import type { MediaRequest } from '../../../../server/entity/MediaRequest'; -import { - useIntl, - FormattedDate, - FormattedRelativeTime, - defineMessages, -} from 'react-intl'; +import { useIntl, FormattedRelativeTime, defineMessages } from 'react-intl'; import { useUser, Permission } from '../../../hooks/useUser'; import { LanguageContext } from '../../../context/LanguageContext'; import type { MovieDetails } from '../../../../server/models/Movie'; @@ -14,7 +9,6 @@ import type { TvDetails } from '../../../../server/models/Tv'; import useSWR from 'swr'; import Badge from '../../Common/Badge'; import StatusBadge from '../../StatusBadge'; -import Table from '../../Common/Table'; import { MediaRequestStatus, MediaStatus, @@ -25,11 +19,16 @@ import globalMessages from '../../../i18n/globalMessages'; import Link from 'next/link'; import { useToasts } from 'react-toast-notifications'; import RequestModal from '../../RequestModal'; +import ConfirmButton from '../../Common/ConfirmButton'; const messages = defineMessages({ seasons: 'Seasons', notavailable: 'N/A', failedretry: 'Something went wrong while retrying the request.', + areyousure: 'Are you sure?', + status: 'Status', + requested: 'Requested', + modifiedby: 'Modified By', }); const isMovie = (movie: MovieDetails | TvDetails): movie is MovieDetails => { @@ -101,22 +100,24 @@ const RequestItem: React.FC = ({ if (!title && !error) { return ( - - - +
); } if (!title || !requestData) { return ( - - - +
); } return ( - + <> = ({ setShowEditModal(false); }} /> - -
- +
+
+ - - - - -
+ alt="" + className="h-full transition duration-300 scale-100 rounded-md shadow-sm cursor-pointer w-14 lg:w-auto lg:h-full transform-gpu hover:scale-105 hover:shadow-md" + /> +
= ({ : `/tv/${requestData.media.tmdbId}` } > - + {isMovie(title) ? title.title : title.name} - + = ({ {requestData.seasons.length > 0 && ( -
+
{intl.formatMessage(messages.seasons)} @@ -188,191 +185,204 @@ const RequestItem: React.FC = ({ )}
- - - {requestData.media[requestData.is4k ? 'status4k' : 'status'] === - MediaStatus.UNKNOWN || - requestData.status === MediaRequestStatus.DECLINED ? ( - - {requestData.status === MediaRequestStatus.DECLINED - ? intl.formatMessage(globalMessages.declined) - : intl.formatMessage(globalMessages.failed)} - - ) : ( - 0 - } - is4k={requestData.is4k} - plexUrl={requestData.media.plexUrl} - plexUrl4k={requestData.media.plexUrl4k} - /> - )} - - -
- - - -
-
- -
- {requestData.modifiedBy ? ( - -
- - - {requestData.modifiedBy.displayName} ( - - ) +
+
+ {intl.formatMessage(messages.status)} + {requestData.media[requestData.is4k ? 'status4k' : 'status'] === + MediaStatus.UNKNOWN || + requestData.status === MediaRequestStatus.DECLINED ? ( + + {requestData.status === MediaRequestStatus.DECLINED + ? intl.formatMessage(globalMessages.declined) + : intl.formatMessage(globalMessages.failed)} + + ) : ( + 0 + } + is4k={requestData.is4k} + plexUrl={requestData.media.plexUrl} + plexUrl4k={requestData.media.plexUrl4k} + /> + )} +
+
+ + {intl.formatMessage(messages.requested)} + + + {intl.formatDate(requestData.createdAt)} + +
+
+ + {intl.formatMessage(messages.modifiedby)} + + + {requestData.modifiedBy ? ( + + + + + + {requestData.modifiedBy.displayName} ( + + ) + + + -
+ ) : ( + N/A + )} - ) : ( - N/A - )} +
- - - {requestData.media[requestData.is4k ? 'status4k' : 'status'] === - MediaStatus.UNKNOWN && - requestData.status !== MediaRequestStatus.DECLINED && - hasPermission(Permission.MANAGE_REQUESTS) && ( - - )} - {requestData.status !== MediaRequestStatus.PENDING && - hasPermission(Permission.MANAGE_REQUESTS) && ( - + )} + {requestData.status !== MediaRequestStatus.PENDING && + hasPermission(Permission.MANAGE_REQUESTS) && ( + deleteRequest()} + confirmText={intl.formatMessage(messages.areyousure)} + className="w-full" > - - - - {intl.formatMessage(globalMessages.delete)} - - - )} - {requestData.status === MediaRequestStatus.PENDING && - hasPermission(Permission.MANAGE_REQUESTS) && ( - <> - - - -
- - - - - - - - - )} - - + + + + + {intl.formatMessage(globalMessages.edit)} + + + + + )} +
+
+ ); }; diff --git a/src/components/RequestList/index.tsx b/src/components/RequestList/index.tsx index 0be3bb00..27db650c 100644 --- a/src/components/RequestList/index.tsx +++ b/src/components/RequestList/index.tsx @@ -1,20 +1,15 @@ -import React, { useState } from 'react'; +import React, { useEffect, useState } from 'react'; import useSWR from 'swr'; import type { RequestResultsResponse } from '../../../server/interfaces/api/requestInterfaces'; import LoadingSpinner from '../Common/LoadingSpinner'; import RequestItem from './RequestItem'; import Header from '../Common/Header'; -import Table from '../Common/Table'; import Button from '../Common/Button'; import { defineMessages, useIntl } from 'react-intl'; import PageTitle from '../Common/PageTitle'; const messages = defineMessages({ requests: 'Requests', - mediaInfo: 'Media Info', - status: 'Status', - requestedAt: 'Requested At', - modifiedBy: 'Last Modified By', showingresults: 'Showing {from} to {to} of {total} results', resultsperpage: 'Display {pageSize} results per page', @@ -46,6 +41,32 @@ const RequestList: React.FC = () => { pageIndex * currentPageSize }&filter=${currentFilter}&sort=${currentSort}` ); + + // Restore last set filter values on component mount + useEffect(() => { + const filterString = window.localStorage.getItem('rl-filter-settings'); + + if (filterString) { + const filterSettings = JSON.parse(filterString); + + setCurrentFilter(filterSettings.currentFilter); + setCurrentSort(filterSettings.currentSort); + setCurrentPageSize(filterSettings.currentPageSize); + } + }, []); + + // Set fitler values to local storage any time they are changed + useEffect(() => { + window.localStorage.setItem( + 'rl-filter-settings', + JSON.stringify({ + currentFilter, + currentSort, + currentPageSize, + }) + ); + }, [currentFilter, currentSort, currentPageSize]); + if (!data && !error) { return ; } @@ -60,7 +81,7 @@ const RequestList: React.FC = () => { return ( <> -
+
{intl.formatMessage(messages.requests)}
@@ -140,114 +161,96 @@ const RequestList: React.FC = () => {
- - - - {intl.formatMessage(messages.mediaInfo)} - {intl.formatMessage(messages.status)} - {intl.formatMessage(messages.requestedAt)} - {intl.formatMessage(messages.modifiedBy)} - - - - - {data.results.map((request) => { - return ( - revalidate()} - /> - ); - })} + {data.results.map((request) => { + return ( +
+ revalidate()} + /> +
+ ); + })} - {data.results.length === 0 && ( -
- -
- - {intl.formatMessage(messages.noresults)} - - {currentFilter !== 'all' && ( -
- -
- )} -
-
- - )} - - - - -
+ + + + + + + ), + })} + +
+
+ + +
+ +
); }; diff --git a/src/i18n/locale/en.json b/src/i18n/locale/en.json index c3821d33..eafcb692 100644 --- a/src/i18n/locale/en.json +++ b/src/i18n/locale/en.json @@ -165,27 +165,27 @@ "components.RequestButton.viewrequest4k": "View 4K Request", "components.RequestCard.all": "All", "components.RequestCard.seasons": "Seasons", + "components.RequestList.RequestItem.areyousure": "Are you sure?", "components.RequestList.RequestItem.failedretry": "Something went wrong while retrying the request.", + "components.RequestList.RequestItem.modifiedby": "Modified By", "components.RequestList.RequestItem.notavailable": "N/A", + "components.RequestList.RequestItem.requested": "Requested", "components.RequestList.RequestItem.seasons": "Seasons", + "components.RequestList.RequestItem.status": "Status", "components.RequestList.filterAll": "All", "components.RequestList.filterApproved": "Approved", "components.RequestList.filterAvailable": "Available", "components.RequestList.filterPending": "Pending", "components.RequestList.filterProcessing": "Processing", - "components.RequestList.mediaInfo": "Media Info", - "components.RequestList.modifiedBy": "Last Modified By", "components.RequestList.next": "Next", "components.RequestList.noresults": "No results.", "components.RequestList.previous": "Previous", - "components.RequestList.requestedAt": "Requested At", "components.RequestList.requests": "Requests", "components.RequestList.resultsperpage": "Display {pageSize} results per page", "components.RequestList.showallrequests": "Show All Requests", "components.RequestList.showingresults": "Showing {from} to {to} of {total} results", "components.RequestList.sortAdded": "Request Date", "components.RequestList.sortModified": "Last Modified", - "components.RequestList.status": "Status", "components.RequestModal.AdvancedRequester.advancedoptions": "Advanced Options", "components.RequestModal.AdvancedRequester.animenote": "* This series is an anime.", "components.RequestModal.AdvancedRequester.default": "(Default)",