feat(ui): Add 'Available' filter to request list and remove unused MediaRequestStatus.AVAILABLE enum value (#905)

pull/901/head
TheCatLady 3 years ago committed by GitHub
parent ef5d019c18
commit 9757e3ae0c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -27,13 +27,13 @@ tags:
- name: tv - name: tv
description: Endpoints related to retrieving TV series and their details. description: Endpoints related to retrieving TV series and their details.
- name: person - name: person
description: Endpoints related to retrieving Person details. description: Endpoints related to retrieving person details.
- name: media - name: media
description: Endpoints related to media management. description: Endpoints related to media management.
- name: collection - name: collection
description: Endpoints related to retrieving Collection details. description: Endpoints related to retrieving collection details.
- name: service - name: service
description: Endpoinst related to getting Service (Radarr/Sonarr) details. description: Endpoints related to getting service (Radarr/Sonarr) details.
servers: servers:
- url: '{server}/api/v1' - url: '{server}/api/v1'
variables: variables:
@ -3095,7 +3095,7 @@ paths:
schema: schema:
type: string type: string
nullable: true nullable: true
enum: [all, available, approved, pending, unavailable] enum: [all, approved, available, pending, processing, unavailable]
- in: query - in: query
name: sort name: sort
schema: schema:
@ -3187,6 +3187,12 @@ paths:
approved: approved:
type: number type: number
example: 10 example: 10
processing:
type: number
example: 4
available:
type: number
example: 6
required: required:
- pending - pending
- approved - approved

@ -2,7 +2,6 @@ export enum MediaRequestStatus {
PENDING = 1, PENDING = 1,
APPROVED, APPROVED,
DECLINED, DECLINED,
AVAILABLE,
} }
export enum MediaType { export enum MediaType {

@ -1,7 +1,7 @@
import { Router } from 'express'; import { Router } from 'express';
import { isAuthenticated } from '../middleware/auth'; import { isAuthenticated } from '../middleware/auth';
import { Permission } from '../lib/permissions'; import { Permission } from '../lib/permissions';
import { getRepository, FindOperator, FindOneOptions, In } from 'typeorm'; import { getRepository } from 'typeorm';
import { MediaRequest } from '../entity/MediaRequest'; import { MediaRequest } from '../entity/MediaRequest';
import TheMovieDb from '../api/themoviedb'; import TheMovieDb from '../api/themoviedb';
import Media from '../entity/Media'; import Media from '../entity/Media';
@ -19,61 +19,98 @@ requestRoutes.get('/', async (req, res, next) => {
const pageSize = req.query.take ? Number(req.query.take) : 20; const pageSize = req.query.take ? Number(req.query.take) : 20;
const skip = req.query.skip ? Number(req.query.skip) : 0; const skip = req.query.skip ? Number(req.query.skip) : 0;
let statusFilter: let statusFilter: MediaRequestStatus[];
| MediaRequestStatus
| FindOperator<string | MediaRequestStatus>
| undefined = undefined;
switch (req.query.filter) { switch (req.query.filter) {
case 'available':
statusFilter = MediaRequestStatus.AVAILABLE;
break;
case 'approved': case 'approved':
statusFilter = MediaRequestStatus.APPROVED; case 'processing':
case 'available':
statusFilter = [MediaRequestStatus.APPROVED];
break; break;
case 'pending': case 'pending':
statusFilter = MediaRequestStatus.PENDING; statusFilter = [MediaRequestStatus.PENDING];
break; break;
case 'unavailable': case 'unavailable':
statusFilter = In([ statusFilter = [
MediaRequestStatus.PENDING,
MediaRequestStatus.APPROVED,
];
break;
default:
statusFilter = [
MediaRequestStatus.PENDING, MediaRequestStatus.PENDING,
MediaRequestStatus.APPROVED, MediaRequestStatus.APPROVED,
]); MediaRequestStatus.DECLINED,
];
}
let mediaStatusFilter: MediaStatus[];
switch (req.query.filter) {
case 'available':
mediaStatusFilter = [MediaStatus.AVAILABLE];
break;
case 'processing':
case 'unavailable':
mediaStatusFilter = [
MediaStatus.UNKNOWN,
MediaStatus.PENDING,
MediaStatus.PROCESSING,
MediaStatus.PARTIALLY_AVAILABLE,
];
break; break;
default: default:
statusFilter = In(Object.values(MediaRequestStatus)); mediaStatusFilter = [
MediaStatus.UNKNOWN,
MediaStatus.PENDING,
MediaStatus.PROCESSING,
MediaStatus.PARTIALLY_AVAILABLE,
MediaStatus.AVAILABLE,
];
} }
let sortFilter: FindOneOptions<MediaRequest>['order'] = { let sortFilter: string;
id: 'DESC',
};
switch (req.query.sort) { switch (req.query.sort) {
case 'modified': case 'modified':
sortFilter = { sortFilter = 'request.updatedAt';
updatedAt: 'DESC',
};
break; break;
default:
sortFilter = 'request.id';
} }
const [requests, requestCount] = req.user?.hasPermission( let query = requestRepository
[Permission.MANAGE_REQUESTS, Permission.REQUEST_VIEW], .createQueryBuilder('request')
{ type: 'or' } .leftJoinAndSelect('request.media', 'media')
) .leftJoinAndSelect('request.seasons', 'seasons')
? await requestRepository.findAndCount({ .leftJoinAndSelect('request.modifiedBy', 'modifiedBy')
order: sortFilter, .leftJoinAndSelect('request.requestedBy', 'requestedBy')
relations: ['media', 'modifiedBy'], .where('request.status IN (:...requestStatus)', {
where: { status: statusFilter }, requestStatus: statusFilter,
take: Number(req.query.take) ?? 20, })
skip, .andWhere(
}) '(request.is4k = false AND media.status IN (:...mediaStatus)) OR (request.is4k = true AND media.status4k IN (:...mediaStatus))',
: await requestRepository.findAndCount({ {
where: { requestedBy: { id: req.user?.id }, status: statusFilter }, mediaStatus: mediaStatusFilter,
relations: ['media', 'modifiedBy'], }
order: sortFilter, );
take: Number(req.query.limit) ?? 20,
skip, if (
}); !req.user?.hasPermission(
[Permission.MANAGE_REQUESTS, Permission.REQUEST_VIEW],
{ type: 'or' }
)
) {
query = query.andWhere('request.requestedBy.id = :id', {
id: req.user?.id,
});
}
const [requests, requestCount] = await query
.orderBy(sortFilter, 'DESC')
.take(pageSize)
.skip(skip)
.getManyAndCount();
return res.status(200).json({ return res.status(200).json({
pageInfo: { pageInfo: {
@ -279,16 +316,51 @@ requestRoutes.get('/count', async (_req, res, next) => {
const requestRepository = getRepository(MediaRequest); const requestRepository = getRepository(MediaRequest);
try { try {
const pendingCount = await requestRepository.count({ const query = requestRepository
status: MediaRequestStatus.PENDING, .createQueryBuilder('request')
}); .leftJoinAndSelect('request.media', 'media');
const approvedCount = await requestRepository.count({
status: MediaRequestStatus.APPROVED, const pendingCount = await query
}); .where('request.status = :requestStatus', {
requestStatus: MediaRequestStatus.PENDING,
})
.getCount();
const approvedCount = await query
.where('request.status = :requestStatus', {
requestStatus: MediaRequestStatus.APPROVED,
})
.getCount();
const processingCount = await query
.where('request.status = :requestStatus', {
requestStatus: MediaRequestStatus.APPROVED,
})
.andWhere(
'(request.is4k = false AND media.status != :availableStatus) OR (request.is4k = true AND media.status4k != :availableStatus)',
{
availableStatus: MediaStatus.AVAILABLE,
}
)
.getCount();
const availableCount = await query
.where('request.status = :requestStatus', {
requestStatus: MediaRequestStatus.APPROVED,
})
.andWhere(
'(request.is4k = false AND media.status = :availableStatus) OR (request.is4k = true AND media.status4k = :availableStatus)',
{
availableStatus: MediaStatus.AVAILABLE,
}
)
.getCount();
return res.status(200).json({ return res.status(200).json({
pending: pendingCount, pending: pendingCount,
approved: approvedCount, approved: approvedCount,
processing: processingCount,
available: availableCount,
}); });
} catch (e) { } catch (e) {
next({ status: 500, message: e.message }); next({ status: 500, message: e.message });

@ -22,13 +22,15 @@ const messages = defineMessages({
filterAll: 'All', filterAll: 'All',
filterPending: 'Pending', filterPending: 'Pending',
filterApproved: 'Approved', filterApproved: 'Approved',
filterAvailable: 'Available',
filterProcessing: 'Processing',
noresults: 'No results.', noresults: 'No results.',
showallrequests: 'Show All Requests', showallrequests: 'Show All Requests',
sortAdded: 'Request Date', sortAdded: 'Request Date',
sortModified: 'Last Modified', sortModified: 'Last Modified',
}); });
type Filter = 'all' | 'approved' | 'pending'; type Filter = 'all' | 'pending' | 'approved' | 'processing' | 'available';
type Sort = 'added' | 'modified'; type Sort = 'added' | 'modified';
const RequestList: React.FC = () => { const RequestList: React.FC = () => {
@ -93,6 +95,12 @@ const RequestList: React.FC = () => {
<option value="approved"> <option value="approved">
{intl.formatMessage(messages.filterApproved)} {intl.formatMessage(messages.filterApproved)}
</option> </option>
<option value="processing">
{intl.formatMessage(messages.filterProcessing)}
</option>
<option value="available">
{intl.formatMessage(messages.filterAvailable)}
</option>
</select> </select>
</div> </div>
<div className="flex flex-grow mb-2 sm:mb-0 lg:flex-grow-0"> <div className="flex flex-grow mb-2 sm:mb-0 lg:flex-grow-0">

@ -523,13 +523,6 @@ const TvRequestModal: React.FC<RequestModalProps> = ({
{intl.formatMessage(globalMessages.requested)} {intl.formatMessage(globalMessages.requested)}
</Badge> </Badge>
)} )}
{!mediaSeason &&
seasonRequest?.status ===
MediaRequestStatus.AVAILABLE && (
<Badge badgeType="success">
{intl.formatMessage(globalMessages.available)}
</Badge>
)}
{mediaSeason?.[is4k ? 'status4k' : 'status'] === {mediaSeason?.[is4k ? 'status4k' : 'status'] ===
MediaStatus.PARTIALLY_AVAILABLE && ( MediaStatus.PARTIALLY_AVAILABLE && (
<Badge badgeType="success"> <Badge badgeType="success">

@ -152,7 +152,9 @@
"components.RequestList.RequestItem.seasons": "Seasons", "components.RequestList.RequestItem.seasons": "Seasons",
"components.RequestList.filterAll": "All", "components.RequestList.filterAll": "All",
"components.RequestList.filterApproved": "Approved", "components.RequestList.filterApproved": "Approved",
"components.RequestList.filterAvailable": "Available",
"components.RequestList.filterPending": "Pending", "components.RequestList.filterPending": "Pending",
"components.RequestList.filterProcessing": "Processing",
"components.RequestList.mediaInfo": "Media Info", "components.RequestList.mediaInfo": "Media Info",
"components.RequestList.modifiedBy": "Last Modified By", "components.RequestList.modifiedBy": "Last Modified By",
"components.RequestList.next": "Next", "components.RequestList.next": "Next",

Loading…
Cancel
Save