fix: trpc error at least one integration (#1660)
This commit is contained in:
@@ -2,9 +2,10 @@ import { Card } from "@mantine/core";
|
|||||||
import { useElementSize } from "@mantine/hooks";
|
import { useElementSize } from "@mantine/hooks";
|
||||||
import { QueryErrorResetBoundary } from "@tanstack/react-query";
|
import { QueryErrorResetBoundary } from "@tanstack/react-query";
|
||||||
import combineClasses from "clsx";
|
import combineClasses from "clsx";
|
||||||
|
import { NoIntegrationSelectedError } from "node_modules/@homarr/widgets/src/errors";
|
||||||
import { ErrorBoundary } from "react-error-boundary";
|
import { ErrorBoundary } from "react-error-boundary";
|
||||||
|
|
||||||
import { loadWidgetDynamic, reduceWidgetOptionsWithDefaultValues } from "@homarr/widgets";
|
import { loadWidgetDynamic, reduceWidgetOptionsWithDefaultValues, widgetImports } from "@homarr/widgets";
|
||||||
import { WidgetError } from "@homarr/widgets/errors";
|
import { WidgetError } from "@homarr/widgets/errors";
|
||||||
|
|
||||||
import type { Item } from "~/app/[locale]/boards/_types";
|
import type { Item } from "~/app/[locale]/boards/_types";
|
||||||
@@ -54,11 +55,14 @@ const InnerContent = ({ item, ...dimensions }: InnerContentProps) => {
|
|||||||
const board = useRequiredBoard();
|
const board = useRequiredBoard();
|
||||||
const [isEditMode] = useEditMode();
|
const [isEditMode] = useEditMode();
|
||||||
const Comp = loadWidgetDynamic(item.kind);
|
const Comp = loadWidgetDynamic(item.kind);
|
||||||
|
const { definition } = widgetImports[item.kind];
|
||||||
const options = reduceWidgetOptionsWithDefaultValues(item.kind, item.options);
|
const options = reduceWidgetOptionsWithDefaultValues(item.kind, item.options);
|
||||||
const newItem = { ...item, options };
|
const newItem = { ...item, options };
|
||||||
const { updateItemOptions } = useItemActions();
|
const { updateItemOptions } = useItemActions();
|
||||||
const updateOptions = ({ newOptions }: { newOptions: Record<string, unknown> }) =>
|
const updateOptions = ({ newOptions }: { newOptions: Record<string, unknown> }) =>
|
||||||
updateItemOptions({ itemId: item.id, newOptions });
|
updateItemOptions({ itemId: item.id, newOptions });
|
||||||
|
const widgetSupportsIntegrations =
|
||||||
|
"supportedIntegrations" in definition && definition.supportedIntegrations.length >= 1;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<QueryErrorResetBoundary>
|
<QueryErrorResetBoundary>
|
||||||
@@ -72,6 +76,10 @@ const InnerContent = ({ item, ...dimensions }: InnerContentProps) => {
|
|||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
|
<Throw
|
||||||
|
error={new NoIntegrationSelectedError()}
|
||||||
|
when={widgetSupportsIntegrations && item.integrationIds.length === 0}
|
||||||
|
/>
|
||||||
<BoardItemMenu offset={4} item={newItem} />
|
<BoardItemMenu offset={4} item={newItem} />
|
||||||
<Comp
|
<Comp
|
||||||
options={options as never}
|
options={options as never}
|
||||||
@@ -87,3 +95,8 @@ const InnerContent = ({ item, ...dimensions }: InnerContentProps) => {
|
|||||||
</QueryErrorResetBoundary>
|
</QueryErrorResetBoundary>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const Throw = ({ when, error }: { when: boolean; error: Error }) => {
|
||||||
|
if (when) throw error;
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|||||||
@@ -29,7 +29,6 @@ import { useI18n } from "@homarr/translation/client";
|
|||||||
|
|
||||||
import type { widgetKind } from ".";
|
import type { widgetKind } from ".";
|
||||||
import type { WidgetComponentProps } from "../../definition";
|
import type { WidgetComponentProps } from "../../definition";
|
||||||
import { NoIntegrationSelectedError } from "../../errors";
|
|
||||||
import TimerModal from "./TimerModal";
|
import TimerModal from "./TimerModal";
|
||||||
|
|
||||||
const dnsLightStatus = (enabled: boolean | undefined) =>
|
const dnsLightStatus = (enabled: boolean | undefined) =>
|
||||||
@@ -184,10 +183,6 @@ export default function DnsHoleControlsWidget({
|
|||||||
|
|
||||||
const controlAllButtonsVisible = options.showToggleAllButtons && integrationsWithInteractions.length > 0;
|
const controlAllButtonsVisible = options.showToggleAllButtons && integrationsWithInteractions.length > 0;
|
||||||
|
|
||||||
if (integrationIds.length === 0) {
|
|
||||||
throw new NoIntegrationSelectedError();
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex
|
<Flex
|
||||||
className="dns-hole-controls-stack"
|
className="dns-hole-controls-stack"
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ import type { TablerIcon } from "@homarr/ui";
|
|||||||
|
|
||||||
import type { widgetKind } from ".";
|
import type { widgetKind } from ".";
|
||||||
import type { WidgetComponentProps, WidgetProps } from "../../definition";
|
import type { WidgetComponentProps, WidgetProps } from "../../definition";
|
||||||
import { NoIntegrationSelectedError } from "../../errors";
|
|
||||||
|
|
||||||
export default function DnsHoleSummaryWidget({ options, integrationIds }: WidgetComponentProps<typeof widgetKind>) {
|
export default function DnsHoleSummaryWidget({ options, integrationIds }: WidgetComponentProps<typeof widgetKind>) {
|
||||||
const [summaries] = clientApi.widget.dnsHole.summary.useSuspenseQuery(
|
const [summaries] = clientApi.widget.dnsHole.summary.useSuspenseQuery(
|
||||||
@@ -62,10 +61,6 @@ export default function DnsHoleSummaryWidget({ options, integrationIds }: Widget
|
|||||||
|
|
||||||
const data = useMemo(() => summaries.flatMap(({ summary }) => summary), [summaries]);
|
const data = useMemo(() => summaries.flatMap(({ summary }) => summary), [summaries]);
|
||||||
|
|
||||||
if (integrationIds.length === 0) {
|
|
||||||
throw new NoIntegrationSelectedError();
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box h="100%" {...boxPropsByLayout(options.layout)} p="2cqmin">
|
<Box h="100%" {...boxPropsByLayout(options.layout)} p="2cqmin">
|
||||||
{data.length > 0 ? (
|
{data.length > 0 ? (
|
||||||
|
|||||||
@@ -45,7 +45,6 @@ import type { ExtendedClientStatus, ExtendedDownloadClientItem } from "@homarr/i
|
|||||||
import { useScopedI18n } from "@homarr/translation/client";
|
import { useScopedI18n } from "@homarr/translation/client";
|
||||||
|
|
||||||
import type { WidgetComponentProps } from "../definition";
|
import type { WidgetComponentProps } from "../definition";
|
||||||
import { NoIntegrationSelectedError } from "../errors";
|
|
||||||
|
|
||||||
//Ratio table for relative width between columns
|
//Ratio table for relative width between columns
|
||||||
const columnsRatios: Record<keyof ExtendedDownloadClientItem, number> = {
|
const columnsRatios: Record<keyof ExtendedDownloadClientItem, number> = {
|
||||||
@@ -636,10 +635,6 @@ export default function DownloadClientsWidget({
|
|||||||
{ up: 0, down: 0 },
|
{ up: 0, down: 0 },
|
||||||
);
|
);
|
||||||
|
|
||||||
if (integrationIds.length === 0) {
|
|
||||||
throw new NoIntegrationSelectedError();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options.columns.length === 0)
|
if (options.columns.length === 0)
|
||||||
return (
|
return (
|
||||||
<Center h="100%">
|
<Center h="100%">
|
||||||
|
|||||||
@@ -37,7 +37,6 @@ import type { TranslationFunction } from "@homarr/translation";
|
|||||||
import { useI18n } from "@homarr/translation/client";
|
import { useI18n } from "@homarr/translation/client";
|
||||||
|
|
||||||
import type { WidgetComponentProps } from "../definition";
|
import type { WidgetComponentProps } from "../definition";
|
||||||
import { NoIntegrationSelectedError } from "../errors";
|
|
||||||
|
|
||||||
dayjs.extend(duration);
|
dayjs.extend(duration);
|
||||||
|
|
||||||
@@ -76,9 +75,6 @@ export default function HealthMonitoringWidget({ options, integrationIds }: Widg
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
if (integrationIds.length === 0) {
|
|
||||||
throw new NoIntegrationSelectedError();
|
|
||||||
}
|
|
||||||
return (
|
return (
|
||||||
<Stack h="100%" gap="2.5cqmin" className="health-monitoring">
|
<Stack h="100%" gap="2.5cqmin" className="health-monitoring">
|
||||||
{healthData.map(({ integrationId, integrationName, healthInfo, updatedAt }) => {
|
{healthData.map(({ integrationId, integrationName, healthInfo, updatedAt }) => {
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import { clientApi } from "@homarr/api/client";
|
|||||||
import { useI18n } from "@homarr/translation/client";
|
import { useI18n } from "@homarr/translation/client";
|
||||||
|
|
||||||
import type { WidgetComponentProps } from "../definition";
|
import type { WidgetComponentProps } from "../definition";
|
||||||
import { NoIntegrationSelectedError } from "../errors";
|
|
||||||
|
|
||||||
export default function IndexerManagerWidget({ options, integrationIds }: WidgetComponentProps<"indexerManager">) {
|
export default function IndexerManagerWidget({ options, integrationIds }: WidgetComponentProps<"indexerManager">) {
|
||||||
const t = useI18n();
|
const t = useI18n();
|
||||||
@@ -39,10 +38,6 @@ export default function IndexerManagerWidget({ options, integrationIds }: Widget
|
|||||||
|
|
||||||
const iconStyle = { height: "7.5cqmin", width: "7.5cqmin" };
|
const iconStyle = { height: "7.5cqmin", width: "7.5cqmin" };
|
||||||
|
|
||||||
if (integrationIds.length === 0) {
|
|
||||||
throw new NoIntegrationSelectedError();
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex className="indexer-manager-container" h="100%" direction="column" gap="2.5cqmin" p="2.5cqmin" align="center">
|
<Flex className="indexer-manager-container" h="100%" direction="column" gap="2.5cqmin" p="2.5cqmin" align="center">
|
||||||
<Text className="indexer-manager-title" size="6.5cqmin">
|
<Text className="indexer-manager-title" size="6.5cqmin">
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import type { ScopedTranslationFunction } from "@homarr/translation";
|
|||||||
import { useScopedI18n } from "@homarr/translation/client";
|
import { useScopedI18n } from "@homarr/translation/client";
|
||||||
|
|
||||||
import type { WidgetComponentProps } from "../../definition";
|
import type { WidgetComponentProps } from "../../definition";
|
||||||
import { NoIntegrationSelectedError } from "../../errors";
|
|
||||||
import { NoIntegrationDataError } from "../../errors/no-data-integration";
|
import { NoIntegrationDataError } from "../../errors/no-data-integration";
|
||||||
|
|
||||||
export default function MediaServerWidget({
|
export default function MediaServerWidget({
|
||||||
@@ -58,8 +57,6 @@ export default function MediaServerWidget({
|
|||||||
|
|
||||||
const { mutate: mutateRequestAnswer } = clientApi.widget.mediaRequests.answerRequest.useMutation();
|
const { mutate: mutateRequestAnswer } = clientApi.widget.mediaRequests.answerRequest.useMutation();
|
||||||
|
|
||||||
if (integrationIds.length === 0) throw new NoIntegrationSelectedError();
|
|
||||||
|
|
||||||
if (mediaRequests.length === 0) throw new NoIntegrationDataError();
|
if (mediaRequests.length === 0) throw new NoIntegrationDataError();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ import type { RequestStats } from "@homarr/integrations/types";
|
|||||||
import { useScopedI18n } from "@homarr/translation/client";
|
import { useScopedI18n } from "@homarr/translation/client";
|
||||||
|
|
||||||
import type { WidgetComponentProps } from "../../definition";
|
import type { WidgetComponentProps } from "../../definition";
|
||||||
import { NoIntegrationSelectedError } from "../../errors";
|
|
||||||
import { NoIntegrationDataError } from "../../errors/no-data-integration";
|
import { NoIntegrationDataError } from "../../errors/no-data-integration";
|
||||||
import classes from "./component.module.css";
|
import classes from "./component.module.css";
|
||||||
|
|
||||||
@@ -43,8 +42,6 @@ export default function MediaServerWidget({
|
|||||||
|
|
||||||
const { width, height, ref } = useElementSize();
|
const { width, height, ref } = useElementSize();
|
||||||
|
|
||||||
if (integrationIds.length === 0) throw new NoIntegrationSelectedError();
|
|
||||||
|
|
||||||
if (requestStats.users.length === 0 && requestStats.stats.length === 0) throw new NoIntegrationDataError();
|
if (requestStats.users.length === 0 && requestStats.stats.length === 0) throw new NoIntegrationDataError();
|
||||||
|
|
||||||
//Add processing and available
|
//Add processing and available
|
||||||
|
|||||||
@@ -8,27 +8,16 @@ import { clientApi } from "@homarr/api/client";
|
|||||||
import { useRegisterSpotlightContextActions } from "@homarr/spotlight";
|
import { useRegisterSpotlightContextActions } from "@homarr/spotlight";
|
||||||
|
|
||||||
import type { WidgetComponentProps } from "../../definition";
|
import type { WidgetComponentProps } from "../../definition";
|
||||||
import { NoIntegrationSelectedError } from "../../errors";
|
|
||||||
|
|
||||||
export default function SmartHomeEntityStateWidget({
|
export default function SmartHomeEntityStateWidget({
|
||||||
options,
|
options,
|
||||||
integrationIds,
|
integrationIds,
|
||||||
isEditMode,
|
isEditMode,
|
||||||
}: WidgetComponentProps<"smartHome-entityState">) {
|
}: WidgetComponentProps<"smartHome-entityState">) {
|
||||||
const integrationId = integrationIds[0];
|
// It will always have at least one integration as otherwise the NoIntegrationSelectedError would be thrown in item-content.tsx
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||||
|
const integrationId = integrationIds[0]!;
|
||||||
|
|
||||||
if (!integrationId) {
|
|
||||||
throw new NoIntegrationSelectedError();
|
|
||||||
}
|
|
||||||
|
|
||||||
return <InnerComponent options={options} integrationId={integrationId} isEditMode={isEditMode} />;
|
|
||||||
}
|
|
||||||
|
|
||||||
type InnerComponentProps = Pick<WidgetComponentProps<"smartHome-entityState">, "options" | "isEditMode"> & {
|
|
||||||
integrationId: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
const InnerComponent = ({ options, integrationId, isEditMode }: InnerComponentProps) => {
|
|
||||||
const input = {
|
const input = {
|
||||||
entityId: options.entityId,
|
entityId: options.entityId,
|
||||||
integrationId,
|
integrationId,
|
||||||
@@ -103,4 +92,4 @@ const InnerComponent = ({ options, integrationId, isEditMode }: InnerComponentPr
|
|||||||
</Center>
|
</Center>
|
||||||
</UnstyledButton>
|
</UnstyledButton>
|
||||||
);
|
);
|
||||||
};
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user