"use client"; import { ActionIcon, Avatar, Badge, Box, Card, Grid, Group, ScrollArea, Stack, Text, Tooltip } from "@mantine/core"; import type { Icon } from "@tabler/icons-react"; import { IconDeviceTv, IconHourglass, IconLoaderQuarter, IconMovie, IconPlayerPlay, IconReceipt, IconSearch, IconThumbDown, IconThumbUp, } from "@tabler/icons-react"; import combineClasses from "clsx"; import { clientApi } from "@homarr/api/client"; import { useRequiredBoard } from "@homarr/boards/context"; import type { RequestStats } 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 { NoIntegrationDataError } from "../../errors/no-data-integration"; import classes from "./component.module.css"; import searchClasses from "../search-button.module.css"; const OVERSEERR_COLOR = "#ECB000"; const JELLYSEERR_COLOR = "#6677CC"; export default function MediaServerWidget({ integrationIds, isEditMode, width, height, }: WidgetComponentProps<"mediaRequests-requestStats">) { const t = useI18n("widget.mediaRequests-requestStats"); const tCommon = useI18n("common"); const requestStatsQuery = clientApi.widget.mediaRequests.getStats.useQuery({ integrationIds }); const requestStats = getUsableWidgetQueryData(requestStatsQuery); const board = useRequiredBoard(); if (isInitialWidgetQueryPending(requestStatsQuery)) return ; if (!requestStats) return ; if ( requestStats.users.length === 0 && requestStats.stats.length === 0 && requestStats.failedIntegrations.length === 0 ) throw new NoIntegrationDataError(); const data = [ { name: "approved", icon: IconThumbUp, number: requestStats.stats.reduce((count, { approved }) => count + approved, 0), }, { name: "pending", icon: IconHourglass, number: requestStats.stats.reduce((count, { pending }) => count + pending, 0), }, { name: "processing", icon: IconLoaderQuarter, number: requestStats.stats.reduce((count, { processing }) => count + processing, 0), }, { name: "declined", icon: IconThumbDown, number: requestStats.stats.reduce((count, { declined }) => count + declined, 0), }, { name: "available", icon: IconPlayerPlay, number: requestStats.stats.reduce((count, { available }) => count + available, 0), }, { name: "tv", icon: IconDeviceTv, number: requestStats.stats.reduce((count, { tv }) => count + tv, 0), }, { name: "movie", icon: IconMovie, number: requestStats.stats.reduce((count, { movie }) => count + movie, 0), }, { name: "total", icon: IconReceipt, number: requestStats.stats.reduce((count, { total }) => count + total, 0), }, ] satisfies { name: keyof RequestStats; icon: Icon; number: number }[]; const isTiny = width < 256; const showIntegrationLegend = requestStats.integrations.length > 1 && height >= 240; return ( {!isEditMode && } {t("titles.stats.main")} {data.map((stat) => ( {stat.number} ))} {t("titles.users.main")} ({t("titles.users.requests")}) {requestStats.users.map((user) => { const href = getSafeApplicationUrl(user.link); return ( {user.displayName} {user.requestCount} ); })} {showIntegrationLegend && ( {requestStats.integrations.map((integration) => ( {integration.name} ))} )} ); } const MediaRequestSearchButton = ({ integrationIds }: { integrationIds: string[] }) => { const t = useI18n("search.mode.media"); return ( openMediaRequestSearch({ integrationIds })} > ); };