fix: address widget review findings
This commit is contained in:
1 parent
a3a89db727
commit
f38eb034da
20 files changed
+280
-142
No files matched your search
@@ -1,25 +1,19 @@
|
||||
"use client";
|
||||
|
||||
import { useMemo } from "react";
|
||||
import { Box, Card, Center, SimpleGrid, Text } from "@mantine/core";
|
||||
import dayjs from "dayjs";
|
||||
import duration from "dayjs/plugin/duration";
|
||||
import objectSupport from "dayjs/plugin/objectSupport";
|
||||
import relativeTime from "dayjs/plugin/relativeTime";
|
||||
import { Badge, Card, Center, Group, ScrollArea, SimpleGrid, Stack, Text } from "@mantine/core";
|
||||
|
||||
import { clientApi } from "@homarr/api/client";
|
||||
import { useScopedI18n } from "@homarr/translation/client";
|
||||
import { useCurrentIntlLocale, useScopedI18n } from "@homarr/translation/client";
|
||||
|
||||
import type { WidgetComponentProps } from "../../definition";
|
||||
import { IntegrationErrorIndicator } from "../../common/integration-error-indicator";
|
||||
import { getUsableWidgetQueryData } from "../../common/query-state";
|
||||
import { WidgetQueryErrorIndicator } from "../../common/query-state-indicator";
|
||||
import { getNetworkControllerStatusLayout } from "./layout";
|
||||
import { WifiVariant } from "./variants/wifi-variant";
|
||||
import { WiredVariant } from "./variants/wired-variant";
|
||||
|
||||
dayjs.extend(objectSupport);
|
||||
dayjs.extend(relativeTime);
|
||||
dayjs.extend(duration);
|
||||
|
||||
export default function NetworkControllerNetworkStatusWidget({
|
||||
options,
|
||||
integrationIds,
|
||||
@@ -27,40 +21,89 @@ export default function NetworkControllerNetworkStatusWidget({
|
||||
width,
|
||||
height,
|
||||
}: WidgetComponentProps<"networkControllerStatus">) {
|
||||
const {
|
||||
data: summaries,
|
||||
error,
|
||||
isPending,
|
||||
} = clientApi.widget.networkController.summary.useQuery({
|
||||
const summaryQuery = clientApi.widget.networkController.summary.useQuery({
|
||||
integrationIds,
|
||||
});
|
||||
const results = getUsableWidgetQueryData(summaryQuery) ?? [];
|
||||
const summaries = useMemo(
|
||||
() =>
|
||||
results.filter(
|
||||
(result): result is typeof result & { summary: NonNullable<typeof result.summary> } => result.summary !== null,
|
||||
),
|
||||
[results],
|
||||
);
|
||||
const t = useScopedI18n("widget.networkControllerStatus");
|
||||
const tCommon = useScopedI18n("common");
|
||||
const locale = useCurrentIntlLocale();
|
||||
|
||||
const data = useMemo(() => (summaries ?? []).flatMap(({ summary }) => (summary ? [summary] : [])), [summaries]);
|
||||
const countWifiGuests = data.reduce((sum, summary) => sum + summary.wifi.guests, 0);
|
||||
const countWifiUsers = data.reduce((sum, summary) => sum + summary.wifi.users, 0);
|
||||
const countLanGuests = data.reduce((sum, summary) => sum + summary.lan.guests, 0);
|
||||
const countLanUsers = data.reduce((sum, summary) => sum + summary.lan.users, 0);
|
||||
const countWifiGuests = summaries.reduce((sum, { summary }) => sum + summary.wifi.guests, 0);
|
||||
const countWifiUsers = summaries.reduce((sum, { summary }) => sum + summary.wifi.users, 0);
|
||||
const countLanGuests = summaries.reduce((sum, { summary }) => sum + summary.lan.guests, 0);
|
||||
const countLanUsers = summaries.reduce((sum, { summary }) => sum + summary.lan.users, 0);
|
||||
const layout = getNetworkControllerStatusLayout({ width, height, displayMode, content: options.content });
|
||||
const isAdvanced = displayMode === "advanced";
|
||||
const queryIndicators = (
|
||||
<Group gap={0} justify="flex-end">
|
||||
<IntegrationErrorIndicator results={results} />
|
||||
<WidgetQueryErrorIndicator error={summaryQuery.error} label={t("name")} />
|
||||
</Group>
|
||||
);
|
||||
|
||||
if (error && summaries === undefined) throw error;
|
||||
|
||||
if (isPending || data.length === 0) {
|
||||
if (summaryQuery.isPending || summaries.length === 0) {
|
||||
return (
|
||||
<Center h="100%" p="sm">
|
||||
<Text c="dimmed" size="sm" ta="center">
|
||||
{isPending ? tCommon("action.loading") : t("error.integrationsDisconnected")}
|
||||
</Text>
|
||||
</Center>
|
||||
<Stack h="100%" gap={0}>
|
||||
{queryIndicators}
|
||||
<Center p="sm" style={{ flex: 1 }}>
|
||||
<Text c="dimmed" size="sm" ta="center">
|
||||
{summaryQuery.isPending ? tCommon("action.loading") : t("error.integrationsDisconnected")}
|
||||
</Text>
|
||||
</Center>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
if (isAdvanced) {
|
||||
return (
|
||||
<ScrollArea h="100%" p={layout.padding}>
|
||||
{queryIndicators}
|
||||
<SimpleGrid cols={summaries.length > 1 ? layout.sourceColumns : 1} spacing="sm">
|
||||
{summaries.map(({ integration, summary, updatedAt }) => (
|
||||
<Card key={integration.id} withBorder p="sm">
|
||||
<Stack gap="sm">
|
||||
<Group justify="space-between" wrap="nowrap">
|
||||
<Stack gap={0} miw={0}>
|
||||
<Text fw={600} size="sm" truncate="end">
|
||||
{integration.name}
|
||||
</Text>
|
||||
{updatedAt && (
|
||||
<Text size="xs" c="dimmed">
|
||||
{t("advanced.updatedAt", { date: new Date(updatedAt).toLocaleString(locale) })}
|
||||
</Text>
|
||||
)}
|
||||
</Stack>
|
||||
<Badge size="xs" variant="light">
|
||||
{integration.kind}
|
||||
</Badge>
|
||||
</Group>
|
||||
<SimpleGrid cols={layout.columns} spacing="sm">
|
||||
<Card p={layout.cardPadding} withBorder>
|
||||
<WifiVariant countGuests={summary.wifi.guests} countUsers={summary.wifi.users} />
|
||||
</Card>
|
||||
<Card p={layout.cardPadding} withBorder>
|
||||
<WiredVariant countGuests={summary.lan.guests} countUsers={summary.lan.users} />
|
||||
</Card>
|
||||
</SimpleGrid>
|
||||
</Stack>
|
||||
</Card>
|
||||
))}
|
||||
</SimpleGrid>
|
||||
</ScrollArea>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Box p={layout.padding} h="100%" pos="relative">
|
||||
<Box pos="absolute" top={4} right={8} style={{ zIndex: 2 }}>
|
||||
<IntegrationErrorIndicator results={summaries ?? []} />
|
||||
</Box>
|
||||
<Stack p={layout.padding} h="100%" gap={0}>
|
||||
{queryIndicators}
|
||||
<SimpleGrid cols={layout.columns} h="100%" spacing="sm">
|
||||
{layout.showWifi && (
|
||||
<Card p={layout.cardPadding} withBorder={layout.withBorder}>
|
||||
@@ -83,6 +126,6 @@ export default function NetworkControllerNetworkStatusWidget({
|
||||
</Card>
|
||||
)}
|
||||
</SimpleGrid>
|
||||
</Box>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
@@ -22,12 +22,26 @@ describe("network controller status layout", () => {
|
||||
{
|
||||
name: "advanced below wide boundary",
|
||||
input: { width: 559, height: 320, displayMode: "advanced" as const, content: "wifi" as const },
|
||||
expected: { padding: "md", columns: 1, showWifi: true, showWired: true, horizontalStats: false },
|
||||
expected: {
|
||||
padding: "md",
|
||||
columns: 1,
|
||||
sourceColumns: 1,
|
||||
showWifi: true,
|
||||
showWired: true,
|
||||
horizontalStats: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "wide advanced",
|
||||
input: { width: 560, height: 320, displayMode: "advanced" as const, content: "wired" as const },
|
||||
expected: { padding: "md", columns: 2, showWifi: true, showWired: true, horizontalStats: false },
|
||||
expected: {
|
||||
padding: "md",
|
||||
columns: 2,
|
||||
sourceColumns: 1,
|
||||
showWifi: true,
|
||||
showWired: true,
|
||||
horizontalStats: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "advanced at 200 percent zoom",
|
||||
@@ -45,5 +59,8 @@ describe("network controller status layout", () => {
|
||||
expect(
|
||||
getNetworkControllerStatusLayout({ width: 199, height: 119, displayMode: "compact", content: "wifi" }),
|
||||
).toMatchObject({ padding: "xs", horizontalStats: false });
|
||||
expect(
|
||||
getNetworkControllerStatusLayout({ width: 960, height: 320, displayMode: "advanced", content: "wifi" }),
|
||||
).toMatchObject({ sourceColumns: 2 });
|
||||
});
|
||||
});
|
||||
@@ -13,6 +13,7 @@ interface NetworkControllerStatusLayoutInput {
|
||||
export interface NetworkControllerStatusLayout {
|
||||
padding: "xs" | "sm" | "md";
|
||||
columns: 1 | 2;
|
||||
sourceColumns: 1 | 2;
|
||||
showWifi: boolean;
|
||||
showWired: boolean;
|
||||
cardPadding: 0 | "md";
|
||||
@@ -32,6 +33,7 @@ export const getNetworkControllerStatusLayout = ({
|
||||
return {
|
||||
padding: isAdvanced ? "md" : height < 120 ? "xs" : "sm",
|
||||
columns: isAdvanced && width >= 560 ? 2 : 1,
|
||||
sourceColumns: isAdvanced && width >= 960 ? 2 : 1,
|
||||
showWifi: isAdvanced || content === "wifi",
|
||||
showWired: isAdvanced || content === "wired",
|
||||
cardPadding: isAdvanced ? "md" : 0,
|
||||
|
||||
Reference in new issue
Block a user