import { ActionIcon, Badge, Card, Center, Flex, Group, Image, Stack, Text, Tooltip, } from '@mantine/core'; import { notifications } from '@mantine/notifications'; import { IconCheck, IconGitPullRequest, IconThumbDown, IconThumbUp } from '@tabler/icons-react'; import { useTranslation } from 'next-i18next'; import { useConfigContext } from '~/config/provider'; import { api } from '~/utils/api'; import { defineWidget } from '../helper'; import { WidgetLoading } from '../loading'; import { IWidget } from '../widgets'; import { useMediaRequestQuery } from './media-request-query'; import { MediaRequest, MediaRequestStatus } from './media-request-types'; const definition = defineWidget({ id: 'media-requests-list', icon: IconGitPullRequest, options: { replaceLinksWithExternalHost: { type: 'switch', defaultValue: true, }, }, component: MediaRequestListTile, gridstack: { minWidth: 3, minHeight: 2, maxWidth: 12, maxHeight: 12, }, }); export type MediaRequestListWidget = IWidget<(typeof definition)['id'], typeof definition>; interface MediaRequestListWidgetProps { widget: MediaRequestListWidget; } type MediaRequestDecisionVariables = { request: MediaRequest; isApproved: boolean; }; const useMediaRequestDecisionMutation = () => { const { name: configName } = useConfigContext(); const utils = api.useContext(); const { mutateAsync } = api.overseerr.decide.useMutation({ onSuccess() { utils.mediaRequest.all.invalidate(); }, }); const { t } = useTranslation('modules/media-requests-list'); return async (variables: MediaRequestDecisionVariables) => { const action = variables.isApproved ? t('mutation.approving') : t('mutation.declining'); notifications.show({ id: `decide-${variables.request.id}`, color: 'yellow', title: `${action} ${t('mutation.request')}`, message: undefined, loading: true, }); await mutateAsync( { configName: configName!, id: variables.request.id, isApproved: variables.isApproved, }, { onSuccess(_data, variables) { const title = variables.isApproved ? t('mutation.approved') : t('mutation.declined'); notifications.update({ id: `decide-${variables.id}`, color: 'teal', title, message: undefined, icon: , autoClose: 2000, }); }, } ); }; }; function MediaRequestListTile({ widget }: MediaRequestListWidgetProps) { const { t } = useTranslation('modules/media-requests-list'); const { data, isLoading } = useMediaRequestQuery(); // Use mutation to approve or deny a pending request const decideAsync = useMediaRequestDecisionMutation(); if (!data || isLoading) { return ; } if (data.length === 0) { return (
{t('noRequests')}
); } const countPendingApproval = data.filter( (x) => x.status === MediaRequestStatus.PendingApproval ).length; // Return a sorted data by status to show pending first, then the default order const sortedData = data.sort((a: MediaRequest, b: MediaRequest) => { if (a.status === MediaRequestStatus.PendingApproval) { return -1; } if (b.status === MediaRequestStatus.PendingApproval) { return 1; } return 0; }); return ( {countPendingApproval > 0 ? ( {t('pending', { countPendingApproval })} ) : ( {t('nonePending')} )} {sortedData.map((item) => ( poster {item.airDate && {item.airDate.split('-')[0]}} {item.name} requester avatar {item.userName} {item.status === MediaRequestStatus.PendingApproval && ( { notifications.show({ id: `approve ${item.id}`, color: 'yellow', title: t('tooltips.approving'), message: undefined, loading: true, }); await decideAsync({ request: item, isApproved: true, }); }} > { await decideAsync({ request: item, isApproved: false, }); }} > )} ))} ); } const MediaRequestStatusBadge = ({ status }: { status: MediaRequestStatus }) => { const { t } = useTranslation('modules/media-requests-list'); switch (status) { case MediaRequestStatus.Approved: return {t('state.approved')}; case MediaRequestStatus.Declined: return {t('state.declined')}; case MediaRequestStatus.PendingApproval: return {t('state.pendingApproval')}; default: return <>; } }; export default definition;