diff --git a/packages/widgets/src/dns-hole/controls/component.tsx b/packages/widgets/src/dns-hole/controls/component.tsx
index 05a56b4a4..de89e89ce 100644
--- a/packages/widgets/src/dns-hole/controls/component.tsx
+++ b/packages/widgets/src/dns-hole/controls/component.tsx
@@ -1,16 +1,16 @@
"use client";
import { useEffect, useState } from "react";
-import type { BoxProps } from "@mantine/core";
-import { ActionIcon, Badge, Box, Button, Card, Flex, Image, Tooltip, UnstyledButton } from "@mantine/core";
+import { ActionIcon, Badge, Box, Button, Card, Flex, Image, Stack, Text, Tooltip, UnstyledButton } from "@mantine/core";
import { useDisclosure } from "@mantine/hooks";
import { IconClockPause, IconPlayerPlay, IconPlayerStop } from "@tabler/icons-react";
import { clientApi } from "@homarr/api/client";
import { integrationDefs } from "@homarr/definitions";
+import type { TranslationFunction } from "@homarr/translation";
import { useI18n } from "@homarr/translation/client";
-import type { WidgetComponentProps, WidgetProps } from "../../definition";
+import type { WidgetComponentProps } from "../../definition";
import { NoIntegrationSelectedError } from "../../errors";
import TimerModal from "./TimerModal";
@@ -71,9 +71,9 @@ export default function DnsHoleControlsWidget({ options, integrationIds }: Widge
const allDisabled = status.every((item) => !item.enabled);
return (
-
+
{options.showToggleAllButtons && (
-
+
)}
- {data.map((integrationData) =>
- ControlsCard(integrationData.integrationId, integrationData.integrationKind, toggleDns, status, open, t),
- )}
+
+ {data.map((integrationData) =>
+ ControlsCard(integrationData.integrationId, integrationData.integrationKind, toggleDns, status, open, t),
+ )}
+
-
+
);
}
@@ -127,26 +129,24 @@ const ControlsCard = (
toggleDns: (integrationId: string) => void,
status: { integrationId: string; enabled: boolean }[],
open: () => void,
- t: ReturnType,
+ t: TranslationFunction,
) => {
const integrationStatus = status.find((item) => item.integrationId === integrationId);
const isEnabled = integrationStatus?.enabled ?? false;
const integrationDef = integrationKind === "piHole" ? integrationDefs.piHole : integrationDefs.adGuardHome;
return (
-
+
-
+
- {integrationDef.name}
+ {integrationDef.name}
toggleDns(integrationId)}>
- {isEnabled
- ? t("widget.dnsHoleControls.controls.enabled")
- : t("widget.dnsHoleControls.controls.disabled")}
+ {t(`widget.dnsHoleControls.controls.${isEnabled ? "enabled" : "disabled"}`)}
@@ -158,22 +158,3 @@ const ControlsCard = (
);
};
-
-const boxPropsByLayout = (layout: WidgetProps<"dnsHoleControls">["options"]["layout"]): BoxProps => {
- if (layout === "grid") {
- return {
- display: "grid",
- style: {
- gridTemplateColumns: "1fr 1fr",
- gridTemplateRows: "1fr 1fr",
- },
- };
- }
-
- return {
- display: "flex",
- style: {
- flexDirection: layout,
- },
- };
-};
diff --git a/packages/widgets/src/dns-hole/controls/index.ts b/packages/widgets/src/dns-hole/controls/index.ts
index 785bf5ce4..8fb9027bb 100644
--- a/packages/widgets/src/dns-hole/controls/index.ts
+++ b/packages/widgets/src/dns-hole/controls/index.ts
@@ -9,13 +9,6 @@ export const { definition, componentLoader, serverDataLoader } = createWidgetDef
showToggleAllButtons: factory.switch({
defaultValue: true,
}),
- layout: factory.select({
- options: (["grid", "row", "column"] as const).map((value) => ({
- value,
- label: (t) => t(`widget.dnsHoleControls.option.layout.option.${value}.label`),
- })),
- defaultValue: "grid",
- }),
})),
supportedIntegrations: ["piHole", "adGuardHome"],
errors: {
diff --git a/packages/widgets/src/dns-hole/controls/serverData.ts b/packages/widgets/src/dns-hole/controls/serverData.ts
index e70a0ae7a..b9bfe541f 100644
--- a/packages/widgets/src/dns-hole/controls/serverData.ts
+++ b/packages/widgets/src/dns-hole/controls/serverData.ts
@@ -4,7 +4,7 @@ import { api } from "@homarr/api/server";
import type { WidgetProps } from "../../definition";
-export default async function getServerDataAsync({ integrationIds }: WidgetProps<"dnsHoleSummary">) {
+export default async function getServerDataAsync({ integrationIds }: WidgetProps<"dnsHoleControls">) {
if (integrationIds.length === 0) {
return {
initialData: [],
@@ -19,9 +19,9 @@ export default async function getServerDataAsync({ integrationIds }: WidgetProps
return {
initialData: currentDns,
};
- } catch (error) {
+ } catch {
return {
- initialData: undefined,
+ initialData: [],
};
}
}
diff --git a/packages/widgets/src/dns-hole/summary/component.tsx b/packages/widgets/src/dns-hole/summary/component.tsx
index 457ee9072..87b0e0a2b 100644
--- a/packages/widgets/src/dns-hole/summary/component.tsx
+++ b/packages/widgets/src/dns-hole/summary/component.tsx
@@ -1,12 +1,12 @@
"use client";
+import { useMemo } from "react";
import type { BoxProps } from "@mantine/core";
import { Box, Card, Flex, Text } from "@mantine/core";
import { useElementSize } from "@mantine/hooks";
import { IconBarrierBlock, IconPercentage, IconSearch, IconWorldWww } from "@tabler/icons-react";
import type { RouterOutputs } from "@homarr/api";
-import { clientApi } from "@homarr/api/client";
import { formatNumber } from "@homarr/common";
import type { stringOrTranslation, TranslationFunction } from "@homarr/translation";
import { translateIfNecessary } from "@homarr/translation";
@@ -16,27 +16,23 @@ import type { TablerIcon } from "@homarr/ui";
import type { WidgetComponentProps, WidgetProps } from "../../definition";
import { NoIntegrationSelectedError } from "../../errors";
-export default function DnsHoleSummaryWidget({ options, integrationIds }: WidgetComponentProps<"dnsHoleSummary">) {
+export default function DnsHoleSummaryWidget({
+ options,
+ integrationIds,
+ serverData,
+}: WidgetComponentProps<"dnsHoleSummary">) {
const integrationId = integrationIds.at(0);
if (!integrationId) {
throw new NoIntegrationSelectedError();
}
- const [data] = clientApi.widget.dnsHole.summary.useSuspenseQuery(
- {
- integrationIds,
- },
- {
- refetchOnMount: false,
- retry: false,
- },
- );
+ const data = useMemo(() => (serverData?.initialData ?? []).flatMap((summary) => summary.summary), [serverData]);
return (
{stats.map((item, index) => (
-
+
))}
);
@@ -45,15 +41,22 @@ export default function DnsHoleSummaryWidget({ options, integrationIds }: Widget
const stats = [
{
icon: IconBarrierBlock,
- value: ({ adsBlockedToday }) => formatNumber(adsBlockedToday, 2),
+ value: (data) =>
+ formatNumber(
+ data.reduce((count, { adsBlockedToday }) => count + adsBlockedToday, 0),
+ 2,
+ ),
label: (t) => t("widget.dnsHoleSummary.data.adsBlockedToday"),
color: "rgba(240, 82, 60, 0.4)", // RED
},
{
icon: IconPercentage,
- value: ({ adsBlockedTodayPercentage }, t) =>
+ value: (data, t) =>
t("common.rtl", {
- value: formatNumber(adsBlockedTodayPercentage, 2),
+ value: formatNumber(
+ data.reduce((count, { adsBlockedTodayPercentage }) => count + adsBlockedTodayPercentage, 0),
+ 2,
+ ),
symbol: "%",
}),
label: (t) => t("widget.dnsHoleSummary.data.adsBlockedTodayPercentage"),
@@ -61,13 +64,21 @@ const stats = [
},
{
icon: IconSearch,
- value: ({ dnsQueriesToday }) => formatNumber(dnsQueriesToday, 2),
+ value: (data) =>
+ formatNumber(
+ data.reduce((count, { dnsQueriesToday }) => count + dnsQueriesToday, 0),
+ 2,
+ ),
label: (t) => t("widget.dnsHoleSummary.data.dnsQueriesToday"),
color: "rgba(0, 175, 218, 0.4)", // BLUE
},
{
icon: IconWorldWww,
- value: ({ domainsBeingBlocked }) => formatNumber(domainsBeingBlocked, 2),
+ value: (data) =>
+ formatNumber(
+ data.reduce((count, { domainsBeingBlocked }) => count + domainsBeingBlocked, 0),
+ 2,
+ ),
label: (t) => t("widget.dnsHoleSummary.data.domainsBeingBlocked"),
color: "rgba(0, 176, 96, 0.4)", // GREEN
},
@@ -75,14 +86,14 @@ const stats = [
interface StatItem {
icon: TablerIcon;
- value: (x: RouterOutputs["widget"]["dnsHole"]["summary"], t: TranslationFunction) => string;
+ value: (x: RouterOutputs["widget"]["dnsHole"]["summary"][number]["summary"][], t: TranslationFunction) => string;
label: stringOrTranslation;
color: string;
}
interface StatCardProps {
item: StatItem;
- data: RouterOutputs["widget"]["dnsHole"]["summary"];
+ data: RouterOutputs["widget"]["dnsHole"]["summary"][number]["summary"][];
usePiHoleColors: boolean;
}
const StatCard = ({ item, data, usePiHoleColors }: StatCardProps) => {
diff --git a/packages/widgets/src/dns-hole/summary/serverData.ts b/packages/widgets/src/dns-hole/summary/serverData.ts
index 5f7ce758e..9c31553e5 100644
--- a/packages/widgets/src/dns-hole/summary/serverData.ts
+++ b/packages/widgets/src/dns-hole/summary/serverData.ts
@@ -21,7 +21,7 @@ export default async function getServerDataAsync({ integrationIds }: WidgetProps
};
} catch {
return {
- initialData: undefined,
+ initialData: [],
};
}
}