"use client"; import { Badge, Card, Center, Group, List, ScrollArea, SimpleGrid, Stack, Text, useMantineTheme } from "@mantine/core"; import { IconCircleCheckFilled, IconCircleXFilled } from "@tabler/icons-react"; import dayjs from "dayjs"; import duration from "dayjs/plugin/duration"; import objectSupport from "dayjs/plugin/objectSupport"; import relativeTime from "dayjs/plugin/relativeTime"; import { clientApi } from "@homarr/api/client"; import type { ScopedTranslationFunction } from "@homarr/translation"; import { useCurrentIntlLocale, useI18n } from "@homarr/translation/client"; import type { WidgetComponentProps } from "../../definition"; import { IntegrationErrorIndicator } from "../../common/integration-error-indicator"; import { getBinaryStatusKey } from "../../common/locale"; import { getUsableWidgetQueryData } from "../../common/query-state"; import type { NetworkControllerMatrixMetricKey, NetworkControllerMatrixSectionKey } from "./display"; import { getNetworkControllerMatrix } from "./display"; dayjs.extend(objectSupport); dayjs.extend(relativeTime); dayjs.extend(duration); export default function NetworkControllerSummaryWidget({ integrationIds, width, height, displayMode, }: WidgetComponentProps<"networkControllerSummary">) { const summaryQuery = clientApi.widget.networkController.summary.useQuery({ integrationIds, }); const results = getUsableWidgetQueryData(summaryQuery) ?? []; const summaries = results.filter( (result): result is typeof result & { summary: NonNullable; updatedAt: Date } => result.summary !== null && result.updatedAt !== undefined, ); const { isPending } = summaryQuery; const t = useI18n("widget.networkControllerSummary"); const tDns = useI18n("widget.dnsHoleSummary"); const tCommon = useI18n("common"); const tWidgetCommon = useI18n("widget.common"); const locale = useCurrentIntlLocale(); const statusLabels = { enabled: tDns("status.enabled"), disabled: tDns("status.disabled"), unknown: tDns("status.unknown"), }; const isDense = height < 160; const showDetails = height >= 180 && width >= 240; const columns = width >= 720 ? 2 : 1; const isAdvanced = displayMode === "advanced"; const queryIndicators = ( ); if (isPending || summaries.length === 0) { return ( {queryIndicators}
{isPending ? tCommon("action.loading") : tWidgetCommon("integrationDisconnected")}
); } if (isAdvanced) { const controllerColumns = width >= 960 ? 2 : 1; const matrixColumns = width >= 720 ? 3 : width >= 440 ? 2 : 1; return ( {queryIndicators} {summaries.map(({ integration, summary, updatedAt }) => ( {integration.name} {integration.kind} {t("advanced.updated")} {new Date(updatedAt).toLocaleString(locale)} {getNetworkControllerMatrix(summary).map((section) => ( {getMatrixSectionLabel(section.key, t)} {statusLabels[getBinaryStatusKey(section.status)]} {section.metrics.length > 0 && ( {section.metrics.map((metric) => ( {getMatrixMetricLabel(metric.key, t)} {formatMatrixMetric(metric.key, metric.value)} ))} )} ))} ))} ); } return ( {queryIndicators} {summaries.map(({ integration, summary }) => ( 1} bg="transparent" style={{ borderColor: neutralSurfaceBorder }} > {summaries.length > 1 && ( {integration.name} {integration.kind} )} } > {t("card.wan")} } > {t("card.web")} {summary.www.latency}ms{showDetails ? ` · ${summary.www.ping}ms` : ""} } > {t("card.wifi")} {showDetails ? ` · ${summary.wifi.users + summary.wifi.guests}` : ""} {showDetails && ( } > {t("card.lan")} · {summary.lan.users + summary.lan.guests} )} } > {t("card.vpn.label")} {t("card.vpn.countConnected", { count: summary.vpn.users })} ))} ); } const getMatrixMetricLabel = ( key: NetworkControllerMatrixMetricKey, t: ScopedTranslationFunction<"widget.networkControllerSummary">, ) => t(`advanced.metric.${key}` as never); const formatMatrixMetric = (key: NetworkControllerMatrixMetricKey, value: number) => { if (key === "latency" || key === "ping") return `${value}ms`; if (key === "uptime") return dayjs.duration(value, "seconds").humanize(); return String(value); }; const getMatrixSectionLabel = ( key: NetworkControllerMatrixSectionKey, t: ScopedTranslationFunction<"widget.networkControllerSummary">, ) => { if (key === "vpn") return t("card.vpn.label"); return t(`card.${key}` as never); }; const neutralSurfaceBorder = "rgb(from var(--mantine-color-default-border) r g b / calc(var(--opacity, 1) * 0.45))"; const StatusIcon = ({ status, label, size, }: { status?: "enabled" | "disabled"; label: string; size: number | string; }) => { const mantineTheme = useMantineTheme(); if (status === "enabled") { return ; } if (status === "disabled") { return ; } return ; };