"use client"; import { ActionIcon, Anchor, Avatar, Badge, Card, Group, Image, ScrollArea, Stack, Text, Tooltip } from "@mantine/core"; import { IconThumbDown, IconThumbUp } from "@tabler/icons-react"; import { clientApi } from "@homarr/api/client"; import { useRequiredBoard } from "@homarr/boards/context"; import { MediaAvailability, MediaRequestStatus } from "@homarr/integrations/types"; import type { ScopedTranslationFunction } from "@homarr/translation"; import { useScopedI18n } from "@homarr/translation/client"; import type { WidgetComponentProps } from "../../definition"; import { NoIntegrationDataError } from "../../errors/no-data-integration"; export default function MediaServerWidget({ integrationIds, isEditMode, options, }: WidgetComponentProps<"mediaRequests-requestList">) { const t = useScopedI18n("widget.mediaRequests-requestList"); const [mediaRequests] = clientApi.widget.mediaRequests.getLatestRequests.useSuspenseQuery( { integrationIds, }, { refetchOnMount: false, refetchOnWindowFocus: false, refetchOnReconnect: false, }, ); const utils = clientApi.useUtils(); clientApi.widget.mediaRequests.subscribeToLatestRequests.useSubscription( { integrationIds, }, { onData(data) { utils.widget.mediaRequests.getLatestRequests.setData({ integrationIds }, (prevData) => { if (!prevData) return []; const filteredData = prevData.filter(({ integrationId }) => integrationId !== data.integrationId); const newData = filteredData.concat( data.requests.map((request) => ({ ...request, integrationId: data.integrationId })), ); return newData.sort((dataA, dataB) => { if (dataA.status === dataB.status) { return dataB.createdAt.getTime() - dataA.createdAt.getTime(); } return dataA.status - dataB.status; }); }); }, }, ); const { mutate: mutateRequestAnswer } = clientApi.widget.mediaRequests.answerRequest.useMutation(); const board = useRequiredBoard(); if (mediaRequests.length === 0) throw new NoIntegrationDataError(); return ( {mediaRequests.map((mediaRequest) => ( {mediaRequest.airDate?.getFullYear() ?? t("toBeDetermined")} {getAvailabilityProperties(mediaRequest.availability, t).label} {mediaRequest.name || "unknown"} {(mediaRequest.requestedBy?.displayName ?? "") || "unknown"} {mediaRequest.status === MediaRequestStatus.PendingApproval ? ( { mutateRequestAnswer({ integrationId: mediaRequest.integrationId, requestId: mediaRequest.id, answer: "approve", }); }} > { mutateRequestAnswer({ integrationId: mediaRequest.integrationId, requestId: mediaRequest.id, answer: "decline", }); }} > ) : ( )} ))} ); } const statusMapping = { [MediaRequestStatus.PendingApproval]: { color: "blue", label: (t) => t("pending") }, [MediaRequestStatus.Approved]: { color: "green", label: (t) => t("approved") }, [MediaRequestStatus.Declined]: { color: "red", label: (t) => t("declined") }, [MediaRequestStatus.Failed]: { color: "red", label: (t) => t("failed") }, } satisfies Record< MediaRequestStatus, { color: string; label: (t: ScopedTranslationFunction<"widget.mediaRequests-requestList.status">) => string; } >; interface StatusBadgeProps { status: MediaRequestStatus; } const StatusBadge = ({ status }: StatusBadgeProps) => { const { color, label } = statusMapping[status]; const tStatus = useScopedI18n("widget.mediaRequests-requestList.status"); return ( {label(tStatus)} ); }; function getAvailabilityProperties( mediaRequestAvailability: MediaAvailability, t: ScopedTranslationFunction<"widget.mediaRequests-requestList">, ) { switch (mediaRequestAvailability) { case MediaAvailability.Available: return { color: "green", label: t("availability.available") }; case MediaAvailability.PartiallyAvailable: return { color: "yellow", label: t("availability.partiallyAvailable") }; case MediaAvailability.Processing: return { color: "blue", label: t("availability.processing") }; case MediaAvailability.Pending: return { color: "violet", label: t("availability.pending") }; default: return { color: "red", label: t("availability.unknown") }; } }