"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") };
}
}