"use client"; import dynamic from "next/dynamic"; import { Button, Center, Loader, Stack, Text } from "@mantine/core"; import { IconAlertTriangle } from "@tabler/icons-react"; import { clientApi } from "@homarr/api/client"; import { useSession } from "@homarr/auth/client"; import { useScopedI18n } from "@homarr/translation/client"; import { Link } from "@homarr/ui"; import type { WidgetComponentProps } from "../definition"; import { isLegacyCustomWidgetMigrationError, isTerminalCustomWidgetDefinitionError } from "./migration-state"; const CustomJsxDisplay = dynamic(() => import("./custom-jsx-display"), { ssr: false }); export default function CustomApiWidget({ options, itemId, isEditMode, removeItem, }: WidgetComponentProps<"customApi">) { const t = useScopedI18n("widget.customApi"); const tCustomJsx = useScopedI18n("widget.customApi.customJsx"); const { data: session } = useSession(); const isAdmin = session?.user.permissions.includes("admin") ?? false; const definitionId = options.definitionId; const safeInterval = Number.isFinite(options.refreshInterval) ? options.refreshInterval : 30; const intervalMs = Math.max(1_000, safeInterval * 1_000); const query = clientApi.widget.customApi.getData.useQuery( { itemId: itemId ?? "" }, { enabled: Boolean(itemId) && Boolean(definitionId), refetchInterval: (currentQuery) => isTerminalCustomWidgetDefinitionError(currentQuery.state.error) ? false : intervalMs, retry: (failureCount, error) => !isTerminalCustomWidgetDefinitionError(error) && failureCount < 3, }, ); if (!definitionId) { return ; } if (isEditMode && !itemId) return ; if (query.isLoading) return (
); if (query.error) { const errorCode = query.error.data?.code; const migrationRequired = isLegacyCustomWidgetMigrationError(query.error); if (isEditMode && errorCode === "NOT_FOUND") { return ; } const isUnavailable = errorCode === "NOT_FOUND" || errorCode === "FORBIDDEN"; return ( ); } if (!query.data) return null; return ( ); } function Unavailable({ message, danger = false, removeLabel, actionLabel, actionHref, supplementaryMessage, onRemove, }: { message: string; danger?: boolean; removeLabel?: string; actionLabel?: string; actionHref?: string; supplementaryMessage?: string; onRemove?: () => void; }) { return (
{message} {supplementaryMessage && ( {supplementaryMessage} )} {actionLabel && actionHref && ( )} {removeLabel && onRemove && ( )}
); }