Rework Media Request Stats Widget (#1344)

*  Rework Media Request Stats Widget

* 🎨 More code to do it better than last commit

* ♻️ Resize improvement

* 🐛 Empty Username handling

* 🎨 widget as router input

*  Open links in new tab + media request scrollArea
This commit is contained in:
Tagaishi
2023-09-01 22:15:40 +02:00
committed by GitHub
parent 1bb1a8f628
commit 371587c62d
7 changed files with 368 additions and 188 deletions

View File

@@ -6,6 +6,7 @@ import {
Flex,
Group,
Image,
ScrollArea,
Stack,
Text,
Tooltip,
@@ -30,6 +31,10 @@ const definition = defineWidget({
type: 'switch',
defaultValue: true,
},
openInNewTab: {
type: 'switch',
defaultValue: true,
},
},
component: MediaRequestListTile,
gridstack: {
@@ -55,7 +60,8 @@ const useMediaRequestDecisionMutation = () => {
const utils = api.useContext();
const { mutateAsync } = api.overseerr.decide.useMutation({
onSuccess() {
utils.mediaRequest.all.invalidate();
utils.mediaRequest.allMedia.invalidate();
utils.mediaRequest.users.invalidate();
},
});
const { t } = useTranslation('modules/media-requests-list');
@@ -93,7 +99,7 @@ const useMediaRequestDecisionMutation = () => {
function MediaRequestListTile({ widget }: MediaRequestListWidgetProps) {
const { t } = useTranslation('modules/media-requests-list');
const { data, isLoading } = useMediaRequestQuery();
const { data, isLoading } = useMediaRequestQuery(widget);
// Use mutation to approve or deny a pending request
const decideAsync = useMediaRequestDecisionMutation();
@@ -125,115 +131,118 @@ function MediaRequestListTile({ widget }: MediaRequestListWidgetProps) {
});
return (
<Stack>
{countPendingApproval > 0 ? (
<Text>{t('pending', { countPendingApproval })}</Text>
) : (
<Text>{t('nonePending')}</Text>
)}
{sortedData.map((item) => (
<Card withBorder>
<Flex wrap="wrap" justify="space-between" gap="md">
<Flex gap="md">
<Image
src={item.posterPath}
width={30}
height={50}
alt="poster"
radius="xs"
withPlaceholder
/>
<Stack spacing={0}>
<Group spacing="xs">
{item.airDate && <Text>{item.airDate.split('-')[0]}</Text>}
<MediaRequestStatusBadge status={item.status} />
</Group>
<Text
sx={{ cursor: 'pointer', '&:hover': { textDecoration: 'underline' } }}
lineClamp={1}
weight="bold"
component="a"
href={item.href}
>
{item.name}
</Text>
</Stack>
</Flex>
<Stack justify="center">
<Flex gap="xs">
<ScrollArea h="100%">
<Stack>
{countPendingApproval > 0 ? (
<Text>{t('pending', { countPendingApproval })}</Text>
) : (
<Text>{t('nonePending')}</Text>
)}
{sortedData.map((item) => (
<Card radius="md" withBorder>
<Flex wrap="wrap" justify="space-between" gap="md">
<Flex gap="md">
<Image
src={item.userProfilePicture}
width={25}
height={25}
alt="requester avatar"
radius="xl"
src={item.posterPath}
width={30}
height={50}
alt="poster"
radius="xs"
withPlaceholder
/>
<Text
component="a"
href={item.userLink}
sx={{ cursor: 'pointer', '&:hover': { textDecoration: 'underline' } }}
>
{item.userName}
</Text>
<Stack spacing={0}>
<Group spacing="xs">
{item.airDate && <Text>{item.airDate.split('-')[0]}</Text>}
<MediaRequestStatusBadge status={item.status} />
</Group>
<Text
sx={{ cursor: 'pointer', '&:hover': { textDecoration: 'underline' } }}
lineClamp={1}
weight="bold"
component="a"
href={item.href}
>
{item.name}
</Text>
</Stack>
</Flex>
<Stack justify="center">
<Flex gap="xs">
<Image
src={item.userProfilePicture}
width={25}
height={25}
alt="requester avatar"
radius="xl"
withPlaceholder
/>
<Text
component="a"
href={item.userLink}
target={widget.properties.openInNewTab ? "_blank" : "_self"}
sx={{ cursor: 'pointer', '&:hover': { textDecoration: 'underline' } }}
>
{item.userName}
</Text>
</Flex>
{item.status === MediaRequestStatus.PendingApproval && (
<Group>
<Tooltip label={t('tooltips.approve')} withArrow withinPortal>
<ActionIcon
variant="light"
color="green"
onClick={async () => {
notifications.show({
id: `approve ${item.id}`,
color: 'yellow',
title: t('tooltips.approving'),
message: undefined,
loading: true,
});
{item.status === MediaRequestStatus.PendingApproval && (
<Group>
<Tooltip label={t('tooltips.approve')} withArrow withinPortal>
<ActionIcon
variant="light"
color="green"
onClick={async () => {
notifications.show({
id: `approve ${item.id}`,
color: 'yellow',
title: t('tooltips.approving'),
message: undefined,
loading: true,
});
await decideAsync({
request: item,
isApproved: true,
});
}}
>
<IconThumbUp />
</ActionIcon>
</Tooltip>
<Tooltip label={t('tooltips.decline')} withArrow withinPortal>
<ActionIcon
variant="light"
color="red"
onClick={async () => {
await decideAsync({
request: item,
isApproved: false,
});
}}
>
<IconThumbDown />
</ActionIcon>
</Tooltip>
</Group>
)}
</Stack>
</Flex>
await decideAsync({
request: item,
isApproved: true,
});
}}
>
<IconThumbUp />
</ActionIcon>
</Tooltip>
<Tooltip label={t('tooltips.decline')} withArrow withinPortal>
<ActionIcon
variant="light"
color="red"
onClick={async () => {
await decideAsync({
request: item,
isApproved: false,
});
}}
>
<IconThumbDown />
</ActionIcon>
</Tooltip>
</Group>
)}
</Stack>
</Flex>
<Image
src={item.backdropPath}
pos="absolute"
w="100%"
h="100%"
opacity={0.1}
top={0}
left={0}
style={{ pointerEvents: 'none' }}
/>
</Card>
))}
</Stack>
<Image
src={item.backdropPath}
pos="absolute"
w="100%"
h="100%"
opacity={0.1}
top={0}
left={0}
style={{ pointerEvents: 'none' }}
/>
</Card>
))}
</Stack>
</ScrollArea>
);
}