mirror of
https://github.com/sct/overseerr.git
synced 2025-12-27 08:45:06 +01:00
feat: filter by media type on PersonDetails (#1566)
* feat: add person media type filtering * feat: adjust filter to apply to cast and crew * fix: fix layout issue re #1513 * feat: revert unused server side api changes * feat: move media type filtering to client side Updates person details page to filter cast and crew sections by selected media type re #1513
This commit is contained in:
@@ -7,6 +7,7 @@ import TitleCard from '@app/components/TitleCard';
|
||||
import globalMessages from '@app/i18n/globalMessages';
|
||||
import Error from '@app/pages/_error';
|
||||
import defineMessages from '@app/utils/defineMessages';
|
||||
import { CircleStackIcon } from '@heroicons/react/24/solid';
|
||||
import type { PersonCombinedCreditsResponse } from '@server/interfaces/api/personInterfaces';
|
||||
import type { PersonDetails as PersonDetailsType } from '@server/models/Person';
|
||||
import { groupBy } from 'lodash';
|
||||
@@ -25,9 +26,12 @@ const messages = defineMessages('components.PersonDetails', {
|
||||
ascharacter: 'as {character}',
|
||||
});
|
||||
|
||||
type MediaType = 'all' | 'movie' | 'tv';
|
||||
|
||||
const PersonDetails = () => {
|
||||
const intl = useIntl();
|
||||
const router = useRouter();
|
||||
const [currentMediaType, setCurrentMediaType] = useState<string>('all');
|
||||
const { data, error } = useSWR<PersonDetailsType>(
|
||||
`/api/v1/person/${router.query.personId}`
|
||||
);
|
||||
@@ -39,7 +43,11 @@ const PersonDetails = () => {
|
||||
);
|
||||
|
||||
const sortedCast = useMemo(() => {
|
||||
const grouped = groupBy(combinedCredits?.cast ?? [], 'id');
|
||||
const filtered = (combinedCredits?.cast ?? []).filter(
|
||||
(media) =>
|
||||
currentMediaType === 'all' || media.mediaType === currentMediaType
|
||||
);
|
||||
const grouped = groupBy(filtered, 'id');
|
||||
|
||||
const reduced = Object.values(grouped).map((objs) => ({
|
||||
...objs[0],
|
||||
@@ -54,10 +62,14 @@ const PersonDetails = () => {
|
||||
}
|
||||
return 1;
|
||||
});
|
||||
}, [combinedCredits]);
|
||||
}, [combinedCredits, currentMediaType]);
|
||||
|
||||
const sortedCrew = useMemo(() => {
|
||||
const grouped = groupBy(combinedCredits?.crew ?? [], 'id');
|
||||
const filtered = (combinedCredits?.crew ?? []).filter(
|
||||
(media) =>
|
||||
currentMediaType === 'all' || media.mediaType === currentMediaType
|
||||
);
|
||||
const grouped = groupBy(filtered, 'id');
|
||||
|
||||
const reduced = Object.values(grouped).map((objs) => ({
|
||||
...objs[0],
|
||||
@@ -72,7 +84,7 @@ const PersonDetails = () => {
|
||||
}
|
||||
return 1;
|
||||
});
|
||||
}, [combinedCredits]);
|
||||
}, [combinedCredits, currentMediaType]);
|
||||
|
||||
if (!data && !error) {
|
||||
return <LoadingSpinner />;
|
||||
@@ -122,6 +134,29 @@ const PersonDetails = () => {
|
||||
|
||||
const isLoading = !combinedCredits && !errorCombinedCredits;
|
||||
|
||||
const mediaTypePicker = (
|
||||
<div className="mb-2 flex flex-grow sm:mb-0 sm:mr-2 lg:flex-grow-0">
|
||||
<span className="inline-flex cursor-default items-center rounded-l-md border border-r-0 border-gray-500 bg-gray-800 px-3 text-sm text-gray-100">
|
||||
<CircleStackIcon className="h-6 w-6" />
|
||||
</span>
|
||||
<select
|
||||
id="mediaType"
|
||||
name="mediaType"
|
||||
onChange={(e) => {
|
||||
setCurrentMediaType(e.target.value as MediaType);
|
||||
}}
|
||||
value={currentMediaType}
|
||||
className="rounded-r-only"
|
||||
>
|
||||
<option value="all">{intl.formatMessage(globalMessages.all)}</option>
|
||||
<option value="movie">
|
||||
{intl.formatMessage(globalMessages.movies)}
|
||||
</option>
|
||||
<option value="tv">{intl.formatMessage(globalMessages.tvshows)}</option>
|
||||
</select>
|
||||
</div>
|
||||
);
|
||||
|
||||
const cast = (sortedCast ?? []).length > 0 && (
|
||||
<>
|
||||
<div className="slider-header">
|
||||
@@ -235,8 +270,11 @@ const PersonDetails = () => {
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<div className="text-center text-gray-300 lg:text-left">
|
||||
<h1 className="text-3xl text-white lg:text-4xl">{data.name}</h1>
|
||||
<div className="w-full text-center text-gray-300 lg:text-left">
|
||||
<div className="flex w-full items-center justify-between">
|
||||
<h1 className="text-3xl text-white lg:text-4xl">{data.name}</h1>
|
||||
<div className="flex-shrink-0">{mediaTypePicker}</div>
|
||||
</div>
|
||||
<div className="mt-1 mb-2 space-y-1 text-xs text-white sm:text-sm lg:text-base">
|
||||
<div>{personAttributes.join(' | ')}</div>
|
||||
{(data.alsoKnownAs ?? []).length > 0 && (
|
||||
|
||||
Reference in New Issue
Block a user