"use client"; import { ActionIcon, Anchor, Avatar, Badge, Card, Group, Image, ScrollArea, Stack, Text, Tooltip } from "@mantine/core"; import { IconSearch, IconThumbDown, IconThumbUp } from "@tabler/icons-react"; import type { RouterInputs, RouterOutputs } from "@homarr/api"; import { clientApi } from "@homarr/api/client"; import { useIntegrationsWithInteractAccess } from "@homarr/auth/client"; import { useRequiredBoard } from "@homarr/boards/context"; import { toValidDate } from "@homarr/common"; import type { MediaRequestStatus } from "@homarr/integrations/types"; import { mediaAvailabilityConfiguration, mediaRequestStatusConfiguration } from "@homarr/integrations/types"; import { openMediaRequestSearch } from "@homarr/spotlight"; import { useI18n } from "@homarr/translation/client"; import { WidgetEmptyState } from "../../common/empty-state"; import { getSafeApplicationUrl, SAFE_NEW_TAB_REL } from "../../common/application-url"; import { getUsableWidgetQueryData, isInitialWidgetQueryPending } from "../../common/query-state"; import { WidgetQueryLoadingState } from "../../common/query-state-indicator"; import actionTargetClasses from "../../common/action-target.module.css"; import { IntegrationErrorIndicator } from "../../common/integration-error-indicator"; import type { WidgetComponentProps } from "../../definition"; import { MediaRequestsEmptyState } from "../empty-state"; import classes from "./component.module.css"; import searchClasses from "../search-button.module.css"; export default function MediaServerWidget({ integrationIds, isEditMode, options, width, }: WidgetComponentProps<"mediaRequests-requestList">) { const t = useI18n("widget.mediaRequests-requestList"); const interactIntegrationIds = new Set( useIntegrationsWithInteractAccess() .filter(({ id }) => integrationIds.includes(id)) .map(({ id }) => id), ); const mediaRequestQuery = clientApi.widget.mediaRequests.getLatestRequests.useQuery({ integrationIds, statuses: options.statusFilter.length > 0 ? options.statusFilter : ["pending", "approved", "declined", "failed", "completed"], recentDays: options.recentDays, }); const mediaRequestData = getUsableWidgetQueryData(mediaRequestQuery); if (isInitialWidgetQueryPending(mediaRequestQuery)) return ; if (!mediaRequestData) return ; const { requests: mediaRequests, failedIntegrations } = mediaRequestData; if (mediaRequests.length === 0 && failedIntegrations.length === 0) { return ( ); } const showIntegrationSource = new Set(mediaRequests.map(({ integrationId }) => integrationId)).size > 1; return ( {!isEditMode && } {failedIntegrations.length > 0 && ( )} {mediaRequests.map((mediaRequest) => ( ))} ); } const MediaRequestSearchButton = ({ integrationIds }: { integrationIds: string[] }) => { const t = useI18n("search.mode.media"); return ( openMediaRequestSearch({ integrationIds })} > ); }; interface MediaRequestCardProps { request: RouterOutputs["widget"]["mediaRequests"]["getLatestRequests"]["requests"][number]; isTiny: boolean; showIntegrationSource: boolean; canInteract: boolean; options: WidgetComponentProps<"mediaRequests-requestList">["options"]; } const MediaRequestCard = ({ request, isTiny, showIntegrationSource, canInteract, options }: MediaRequestCardProps) => { const board = useRequiredBoard(); const t = useI18n("widget.mediaRequests-requestList"); const requestHref = getSafeApplicationUrl(request.href); const requestedByHref = getSafeApplicationUrl(request.requestedBy?.link); return ( {!isTiny && ( )} {toValidDate(request.airDate)?.getFullYear() ?? t("toBeDetermined")} {!isTiny && ( {t(`availability.${request.availability}`)} )} {showIntegrationSource && !isTiny && ( {request.integration.name} )} {(request.requestedBy?.displayName ?? "") || t("unknown")} {request.name || t("unknown")} {request.status === "pending" ? ( ) : ( )} ); }; interface DecisionButtonsProps { requestId: number; integrationId: string; canInteract: boolean; alwaysVisible: boolean; } const DecisionButtons = ({ requestId, integrationId, canInteract, alwaysVisible }: DecisionButtonsProps) => { const utils = clientApi.useUtils(); const { mutate: mutateRequestAnswer, isPending, error, } = clientApi.widget.mediaRequests.answerRequest.useMutation({ onSettled: () => void utils.widget.mediaRequests.invalidate(), }); const t = useI18n("widget.mediaRequests-requestList"); const handleDecision = (answer: RouterInputs["widget"]["mediaRequests"]["answerRequest"]["answer"]) => { if (!canInteract || isPending) return; mutateRequestAnswer({ integrationId, requestId, answer, }); }; return ( { handleDecision("approve"); }} > { handleDecision("decline"); }} > ); }; interface StatusBadgeProps { status: MediaRequestStatus; } const StatusBadge = ({ status }: StatusBadgeProps) => { const tStatus = useI18n("widget.mediaRequests-requestList.status"); return ( {tStatus(status)} ); };