mirror of
https://github.com/sct/overseerr.git
synced 2025-09-17 17:24:35 +02:00
feat(frontend): slideover initial work
This commit is contained in:
@@ -62,10 +62,56 @@ export class MediaRequest {
|
||||
@AfterInsert()
|
||||
private async updateParentStatus() {
|
||||
const mediaRepository = getRepository(Media);
|
||||
const media = await mediaRepository.findOne({
|
||||
where: { id: this.media.id },
|
||||
relations: ['requests'],
|
||||
});
|
||||
if (!media) {
|
||||
logger.error('No parent media!', { label: 'Media Request' });
|
||||
return;
|
||||
}
|
||||
const seasonRequestRepository = getRepository(SeasonRequest);
|
||||
if (this.status === MediaRequestStatus.APPROVED) {
|
||||
this.media.status = MediaStatus.PROCESSING;
|
||||
mediaRepository.save(this.media);
|
||||
}
|
||||
|
||||
if (
|
||||
this.media.mediaType === MediaType.MOVIE &&
|
||||
this.status === MediaRequestStatus.DECLINED
|
||||
) {
|
||||
this.media.status = MediaStatus.UNKNOWN;
|
||||
mediaRepository.save(this.media);
|
||||
}
|
||||
|
||||
/**
|
||||
* If the media type is TV, and we are declining a request,
|
||||
* we must check if its the only pending request and that
|
||||
* there the current media status is just pending (meaning no
|
||||
* other requests have yet to be approved)
|
||||
*/
|
||||
if (
|
||||
media.mediaType === MediaType.TV &&
|
||||
this.status === MediaRequestStatus.DECLINED &&
|
||||
media.requests.filter(
|
||||
(request) => request.status === MediaRequestStatus.PENDING
|
||||
).length === 0 &&
|
||||
media.status === MediaStatus.PENDING
|
||||
) {
|
||||
media.status = MediaStatus.UNKNOWN;
|
||||
mediaRepository.save(media);
|
||||
}
|
||||
|
||||
// Approve child seasons if parent is approved
|
||||
if (
|
||||
media.mediaType === MediaType.TV &&
|
||||
this.status === MediaRequestStatus.APPROVED
|
||||
) {
|
||||
this.seasons.forEach((season) => {
|
||||
season.status = MediaRequestStatus.APPROVED;
|
||||
seasonRequestRepository.save(season);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@AfterRemove()
|
||||
|
@@ -222,6 +222,10 @@ requestRoutes.get<{
|
||||
|
||||
return res.status(200).json(request);
|
||||
} catch (e) {
|
||||
logger.error('Error processing request update', {
|
||||
label: 'Media Request',
|
||||
message: e.message,
|
||||
});
|
||||
next({ status: 404, message: 'Request not found' });
|
||||
}
|
||||
}
|
||||
|
@@ -34,19 +34,17 @@ const ButtonWithDropdown: React.FC<ButtonWithDropdownProps> = ({
|
||||
...props
|
||||
}) => {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const buttonRef = useRef<HTMLSpanElement>(null);
|
||||
const buttonRef = useRef<HTMLButtonElement>(null);
|
||||
useClickOutside(buttonRef, () => setIsOpen(false));
|
||||
|
||||
return (
|
||||
<span
|
||||
className="relative z-0 inline-flex shadow-sm rounded-md"
|
||||
ref={buttonRef}
|
||||
>
|
||||
<span className="relative z-0 inline-flex shadow-sm rounded-md">
|
||||
<button
|
||||
type="button"
|
||||
className={`relative inline-flex items-center px-4 py-2 text-white bg-indigo-600 hover:bg-indigo-500 text-sm leading-5 font-medium hover:text-white focus:shadow-outline-indigo active:bg-indigo-700 focus:z-10 focus:outline-none focus:shadow-outline-blue transition ease-in-out duration-150 ${
|
||||
children ? 'rounded-l-md' : 'rounded-md'
|
||||
}`}
|
||||
ref={buttonRef}
|
||||
{...props}
|
||||
>
|
||||
{text}
|
||||
|
115
src/components/Common/SlideOver/index.tsx
Normal file
115
src/components/Common/SlideOver/index.tsx
Normal file
@@ -0,0 +1,115 @@
|
||||
/* eslint-disable jsx-a11y/no-static-element-interactions */
|
||||
import React, { useState, useEffect, useRef } from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import Transition from '../../Transition';
|
||||
import { useLockBodyScroll } from '../../../hooks/useLockBodyScroll';
|
||||
import useClickOutside from '../../../hooks/useClickOutside';
|
||||
|
||||
interface SlideOverProps {
|
||||
show?: boolean;
|
||||
title: string;
|
||||
subText?: string;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
const SlideOver: React.FC<SlideOverProps> = ({
|
||||
show = false,
|
||||
title,
|
||||
subText,
|
||||
onClose,
|
||||
children,
|
||||
}) => {
|
||||
const [isMounted, setIsMounted] = useState(false);
|
||||
const slideoverRef = useRef(null);
|
||||
useLockBodyScroll(show);
|
||||
useClickOutside(slideoverRef, () => {
|
||||
onClose();
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
setIsMounted(true);
|
||||
}, []);
|
||||
|
||||
if (!isMounted) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return ReactDOM.createPortal(
|
||||
<Transition
|
||||
show={show}
|
||||
appear
|
||||
enter="opacity-0 transition ease-in-out duration-300"
|
||||
enterFrom="opacity-0"
|
||||
enterTo="opacity-100"
|
||||
leave="opacity-100 transition ease-in-out duration-300"
|
||||
leaveFrom="opacity-100"
|
||||
leaveTo="opacity-0"
|
||||
>
|
||||
<div
|
||||
className={`z-50 fixed inset-0 overflow-hidden bg-opacity-50 bg-cool-gray-800`}
|
||||
>
|
||||
<div className="absolute inset-0 overflow-hidden">
|
||||
<section className="absolute inset-y-0 right-0 pl-10 max-w-full flex">
|
||||
<Transition
|
||||
show={show}
|
||||
appear
|
||||
enter="transform transition ease-in-out duration-500 sm:duration-700"
|
||||
enterFrom="translate-x-full"
|
||||
enterTo="translate-x-0"
|
||||
leave="transform transition ease-in-out duration-500 sm:duration-700"
|
||||
leaveFrom="translate-x-0"
|
||||
leaveTo="translate-x-full"
|
||||
>
|
||||
<div className="w-screen max-w-md" ref={slideoverRef}>
|
||||
<div className="h-full flex flex-col bg-cool-gray-700 shadow-xl overflow-y-scroll">
|
||||
<header className="space-y-1 py-6 px-4 bg-indigo-600 sm:px-6">
|
||||
<div className="flex items-center justify-between space-x-3">
|
||||
<h2 className="text-lg leading-7 font-medium text-white">
|
||||
{title}
|
||||
</h2>
|
||||
<div className="h-7 flex items-center">
|
||||
<button
|
||||
aria-label="Close panel"
|
||||
className="text-indigo-200 hover:text-white transition ease-in-out duration-150"
|
||||
onClick={() => onClose()}
|
||||
>
|
||||
<svg
|
||||
className="h-6 w-6"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth="2"
|
||||
d="M6 18L18 6M6 6l12 12"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{subText && (
|
||||
<div>
|
||||
<p className="text-sm leading-5 text-indigo-300">
|
||||
{subText}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</header>
|
||||
<div className="relative flex-1 py-6 px-4 sm:px-6 text-white">
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Transition>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</Transition>,
|
||||
document.body
|
||||
);
|
||||
};
|
||||
|
||||
export default SlideOver;
|
@@ -18,10 +18,15 @@ import PersonCard from '../PersonCard';
|
||||
import { LanguageContext } from '../../context/LanguageContext';
|
||||
import LoadingSpinner from '../Common/LoadingSpinner';
|
||||
import { useUser, Permission } from '../../hooks/useUser';
|
||||
import { MediaStatus } from '../../../server/constants/media';
|
||||
import {
|
||||
MediaStatus,
|
||||
MediaRequestStatus,
|
||||
} from '../../../server/constants/media';
|
||||
import RequestModal from '../RequestModal';
|
||||
import Badge from '../Common/Badge';
|
||||
import ButtonWithDropdown from '../Common/ButtonWithDropdown';
|
||||
import axios from 'axios';
|
||||
import SlideOver from '../Common/SlideOver';
|
||||
|
||||
const messages = defineMessages({
|
||||
releasedate: 'Release Date',
|
||||
@@ -61,6 +66,7 @@ const MovieDetails: React.FC<MovieDetailsProps> = ({ movie }) => {
|
||||
const intl = useIntl();
|
||||
const { locale } = useContext(LanguageContext);
|
||||
const [showRequestModal, setShowRequestModal] = useState(false);
|
||||
const [showManager, setShowManager] = useState(false);
|
||||
const { data, error, revalidate } = useSWR<MovieDetailsType>(
|
||||
`/api/v1/movie/${router.query.movieId}?language=${locale}`,
|
||||
{
|
||||
@@ -82,7 +88,19 @@ const MovieDetails: React.FC<MovieDetailsProps> = ({ movie }) => {
|
||||
return <div>Broken?</div>;
|
||||
}
|
||||
|
||||
const activeRequest = data?.mediaInfo?.requests?.[0];
|
||||
const activeRequest = data?.mediaInfo?.requests?.find(
|
||||
(request) => request.status === MediaRequestStatus.PENDING
|
||||
);
|
||||
|
||||
const modifyRequest = async (type: 'approve' | 'decline') => {
|
||||
const response = await axios.get(
|
||||
`/api/v1/request/${activeRequest?.id}/${type}`
|
||||
);
|
||||
|
||||
if (response) {
|
||||
revalidate();
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
@@ -102,6 +120,94 @@ const MovieDetails: React.FC<MovieDetailsProps> = ({ movie }) => {
|
||||
}}
|
||||
onCancel={() => setShowRequestModal(false)}
|
||||
/>
|
||||
<SlideOver
|
||||
show={showManager}
|
||||
title="Manage Movie"
|
||||
onClose={() => setShowManager(false)}
|
||||
subText={data.title}
|
||||
>
|
||||
<h3 className="text-xl mb-2">Requests</h3>
|
||||
<div className="bg-cool-gray-600 shadow overflow-hidden rounded-md">
|
||||
<ul>
|
||||
{data.mediaInfo?.requests?.map((request) => (
|
||||
<li key={`manage-request-${request.id}`}>
|
||||
<div className="block">
|
||||
<div className="px-4 py-4 sm:px-6">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="mr-6 flex items-center text-sm leading-5 text-gray-300">
|
||||
<svg
|
||||
className="flex-shrink-0 mr-1.5 h-5 w-5 text-gray-300"
|
||||
fill="currentColor"
|
||||
viewBox="0 0 20 20"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
d="M10 9a3 3 0 100-6 3 3 0 000 6zm-7 9a7 7 0 1114 0H3z"
|
||||
clipRule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
{request.requestedBy.username}
|
||||
</div>
|
||||
<div className="ml-2 flex-shrink-0 flex">
|
||||
<Button buttonSize="sm" buttonType="danger">
|
||||
<svg
|
||||
className="w-3 h-3"
|
||||
fill="currentColor"
|
||||
viewBox="0 0 20 20"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
d="M9 2a1 1 0 00-.894.553L7.382 4H4a1 1 0 000 2v10a2 2 0 002 2h8a2 2 0 002-2V6a1 1 0 100-2h-3.382l-.724-1.447A1 1 0 0011 2H9zM7 8a1 1 0 012 0v6a1 1 0 11-2 0V8zm5-1a1 1 0 00-1 1v6a1 1 0 102 0V8a1 1 0 00-1-1z"
|
||||
clipRule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-2 sm:flex sm:justify-between">
|
||||
<div className="sm:flex">
|
||||
<div className="mr-6 flex items-center text-sm leading-5 text-gray-300">
|
||||
{request.status === MediaRequestStatus.AVAILABLE && (
|
||||
<Badge badgeType="success">Available</Badge>
|
||||
)}
|
||||
{request.status === MediaRequestStatus.APPROVED && (
|
||||
<Badge badgeType="success">Approved</Badge>
|
||||
)}
|
||||
{request.status === MediaRequestStatus.DECLINED && (
|
||||
<Badge badgeType="danger">Declined</Badge>
|
||||
)}
|
||||
{request.status === MediaRequestStatus.PENDING && (
|
||||
<Badge badgeType="warning">Pending</Badge>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-2 flex items-center text-sm leading-5 text-gray-300 sm:mt-0">
|
||||
<svg
|
||||
className="flex-shrink-0 mr-1.5 h-5 w-5 text-gray-300"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 20 20"
|
||||
fill="currentColor"
|
||||
>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
d="M6 2a1 1 0 00-1 1v1H4a2 2 0 00-2 2v10a2 2 0 002 2h12a2 2 0 002-2V6a2 2 0 00-2-2h-1V3a1 1 0 10-2 0v1H7V3a1 1 0 00-1-1zm0 5a1 1 0 000 2h8a1 1 0 100-2H6z"
|
||||
clipRule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
<span>
|
||||
<FormattedDate value={request.createdAt} />
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</SlideOver>
|
||||
<div className="flex flex-col items-center md:flex-row md:items-end pt-4">
|
||||
<div className="mr-4 flex-shrink-0">
|
||||
<img
|
||||
@@ -215,7 +321,9 @@ const MovieDetails: React.FC<MovieDetailsProps> = ({ movie }) => {
|
||||
>
|
||||
{hasPermission(Permission.MANAGE_REQUESTS) && (
|
||||
<>
|
||||
<ButtonWithDropdown.Item>
|
||||
<ButtonWithDropdown.Item
|
||||
onClick={() => modifyRequest('approve')}
|
||||
>
|
||||
<svg
|
||||
className="w-4 mr-1"
|
||||
fill="currentColor"
|
||||
@@ -230,7 +338,9 @@ const MovieDetails: React.FC<MovieDetailsProps> = ({ movie }) => {
|
||||
</svg>
|
||||
Approve
|
||||
</ButtonWithDropdown.Item>
|
||||
<ButtonWithDropdown.Item>
|
||||
<ButtonWithDropdown.Item
|
||||
onClick={() => modifyRequest('decline')}
|
||||
>
|
||||
<svg
|
||||
className="w-4 mr-1"
|
||||
fill="currentColor"
|
||||
@@ -250,7 +360,11 @@ const MovieDetails: React.FC<MovieDetailsProps> = ({ movie }) => {
|
||||
</ButtonWithDropdown>
|
||||
)}
|
||||
{hasPermission(Permission.MANAGE_REQUESTS) && (
|
||||
<Button buttonType="default" className="ml-2">
|
||||
<Button
|
||||
buttonType="default"
|
||||
className="ml-2"
|
||||
onClick={() => setShowManager(true)}
|
||||
>
|
||||
<svg
|
||||
className="w-5"
|
||||
style={{ height: 20 }}
|
||||
@@ -278,13 +392,6 @@ const MovieDetails: React.FC<MovieDetailsProps> = ({ movie }) => {
|
||||
</div>
|
||||
<div className="flex pt-8 text-white flex-col md:flex-row pb-4">
|
||||
<div className="flex-1 md:mr-8">
|
||||
{/* {data.mediaInfo?.status === MediaStatus.PENDING &&
|
||||
hasPermission(Permission.MANAGE_REQUESTS) && (
|
||||
<PendingRequest
|
||||
request={data.request}
|
||||
onUpdate={() => revalidate()}
|
||||
/>
|
||||
)} */}
|
||||
<h2 className="text-xl md:text-2xl">
|
||||
<FormattedMessage {...messages.overview} />
|
||||
</h2>
|
||||
|
@@ -82,7 +82,7 @@ const TitleCard: React.FC<TitleCardProps> = ({
|
||||
>
|
||||
<div className="absolute top-0 h-full w-full bottom-0 left-0 right-0 overflow-hidden shadow-xl">
|
||||
<div
|
||||
className={`absolute left-0 top-0 rounded-tl-md rounded-br-md z-50 ${
|
||||
className={`absolute left-0 top-0 rounded-tl-md rounded-br-md z-40 ${
|
||||
mediaType === 'movie' ? 'bg-blue-500' : 'bg-purple-600'
|
||||
}`}
|
||||
>
|
||||
@@ -92,7 +92,7 @@ const TitleCard: React.FC<TitleCardProps> = ({
|
||||
</div>
|
||||
|
||||
<div
|
||||
className="absolute right-0 top-0 z-50"
|
||||
className="absolute right-0 top-0 z-40"
|
||||
style={{
|
||||
right: '-1px',
|
||||
}}
|
||||
|
@@ -15,6 +15,8 @@ import { TvDetails as TvDetailsType } from '../../../server/models/Tv';
|
||||
import { MediaStatus } from '../../../server/constants/media';
|
||||
import RequestModal from '../RequestModal';
|
||||
import Badge from '../Common/Badge';
|
||||
import ButtonWithDropdown from '../Common/ButtonWithDropdown';
|
||||
import axios from 'axios';
|
||||
|
||||
const messages = defineMessages({
|
||||
userrating: 'User Rating',
|
||||
@@ -28,8 +30,13 @@ const messages = defineMessages({
|
||||
available: 'Available',
|
||||
unavailable: 'Unavailable',
|
||||
request: 'Request',
|
||||
requestmore: 'Request More',
|
||||
pending: 'Pending',
|
||||
overviewunavailable: 'Overview unavailable',
|
||||
approverequests:
|
||||
'Approve {requestCount} {requestCount, plural, one {Request} other {Requests}}',
|
||||
declinerequests:
|
||||
'Decline {requestCount} {requestCount, plural, one {Request} other {Requests}}',
|
||||
});
|
||||
|
||||
interface TvDetailsProps {
|
||||
@@ -51,7 +58,7 @@ enum MediaRequestStatus {
|
||||
}
|
||||
|
||||
const TvDetails: React.FC<TvDetailsProps> = ({ tv }) => {
|
||||
const { hasPermission } = useUser();
|
||||
const { user, hasPermission } = useUser();
|
||||
const router = useRouter();
|
||||
const intl = useIntl();
|
||||
const { locale } = useContext(LanguageContext);
|
||||
@@ -77,6 +84,24 @@ const TvDetails: React.FC<TvDetailsProps> = ({ tv }) => {
|
||||
return <div>Broken?</div>;
|
||||
}
|
||||
|
||||
const activeRequests = data.mediaInfo?.requests?.filter(
|
||||
(request) => request.status === MediaRequestStatus.PENDING
|
||||
);
|
||||
|
||||
const modifyRequests = async (type: 'approve' | 'decline'): Promise<void> => {
|
||||
if (!activeRequests) {
|
||||
return;
|
||||
}
|
||||
|
||||
await Promise.all(
|
||||
activeRequests.map(async (request) => {
|
||||
return axios.get(`/api/v1/request/${request.id}/${type}`);
|
||||
})
|
||||
);
|
||||
|
||||
revalidate();
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
className="bg-cover bg-center -mx-4 -mt-2 px-4 sm:px-8 pt-4 "
|
||||
@@ -128,7 +153,7 @@ const TvDetails: React.FC<TvDetailsProps> = ({ tv }) => {
|
||||
</div>
|
||||
<div className="flex-1 flex justify-end mt-4 md:mt-0">
|
||||
{(!data.mediaInfo ||
|
||||
data.mediaInfo.status !== MediaStatus.AVAILABLE) && (
|
||||
data.mediaInfo.status === MediaStatus.UNKNOWN) && (
|
||||
<Button
|
||||
buttonType="primary"
|
||||
onClick={() => setShowRequestModal(true)}
|
||||
@@ -150,6 +175,91 @@ const TvDetails: React.FC<TvDetailsProps> = ({ tv }) => {
|
||||
<FormattedMessage {...messages.request} />
|
||||
</Button>
|
||||
)}
|
||||
{data.mediaInfo &&
|
||||
data.mediaInfo.status !== MediaStatus.UNKNOWN &&
|
||||
data.mediaInfo.status !== MediaStatus.AVAILABLE && (
|
||||
<ButtonWithDropdown
|
||||
dropdownIcon={
|
||||
<svg
|
||||
className="w-5 h-5"
|
||||
fill="currentColor"
|
||||
viewBox="0 0 20 20"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
d="M11.3 1.046A1 1 0 0112 2v5h4a1 1 0 01.82 1.573l-7 10A1 1 0 018 18v-5H4a1 1 0 01-.82-1.573l7-10a1 1 0 011.12-.38z"
|
||||
clipRule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
}
|
||||
text={
|
||||
<>
|
||||
<svg
|
||||
className="w-4 mr-1"
|
||||
fill="currentColor"
|
||||
viewBox="0 0 20 20"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z"
|
||||
clipRule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
<FormattedMessage {...messages.requestmore} />
|
||||
</>
|
||||
}
|
||||
onClick={() => setShowRequestModal(true)}
|
||||
>
|
||||
{hasPermission(Permission.MANAGE_REQUESTS) &&
|
||||
activeRequests &&
|
||||
activeRequests.length > 0 && (
|
||||
<>
|
||||
<ButtonWithDropdown.Item
|
||||
onClick={() => modifyRequests('approve')}
|
||||
>
|
||||
<svg
|
||||
className="w-4 mr-1"
|
||||
fill="currentColor"
|
||||
viewBox="0 0 20 20"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z"
|
||||
clipRule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
<FormattedMessage
|
||||
{...messages.approverequests}
|
||||
values={{ requestCount: activeRequests.length }}
|
||||
/>
|
||||
</ButtonWithDropdown.Item>
|
||||
<ButtonWithDropdown.Item
|
||||
onClick={() => modifyRequests('decline')}
|
||||
>
|
||||
<svg
|
||||
className="w-4 mr-1"
|
||||
fill="currentColor"
|
||||
viewBox="0 0 20 20"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z"
|
||||
clipRule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
<FormattedMessage
|
||||
{...messages.declinerequests}
|
||||
values={{ requestCount: activeRequests.length }}
|
||||
/>
|
||||
</ButtonWithDropdown.Item>
|
||||
</>
|
||||
)}
|
||||
</ButtonWithDropdown>
|
||||
)}
|
||||
{hasPermission(Permission.MANAGE_REQUESTS) && (
|
||||
<Button buttonType="default" className="ml-2">
|
||||
<svg
|
||||
@@ -179,13 +289,6 @@ const TvDetails: React.FC<TvDetailsProps> = ({ tv }) => {
|
||||
</div>
|
||||
<div className="flex pt-8 text-white flex-col md:flex-row pb-4">
|
||||
<div className="flex-1 md:mr-8">
|
||||
{/* {data.mediaInfo?.status === MediaStatus.PENDING &&
|
||||
hasPermission(Permission.MANAGE_REQUESTS) && (
|
||||
<PendingRequest
|
||||
request={data.request}
|
||||
onUpdate={() => revalidate()}
|
||||
/>
|
||||
)} */}
|
||||
<h2 className="text-xl md:text-2xl">
|
||||
<FormattedMessage {...messages.overview} />
|
||||
</h2>
|
||||
|
Reference in New Issue
Block a user