"use client"; import { ActionIcon, Avatar, Badge, Card, Center, Group, RingProgress, Stack, Text, Tooltip } from "@mantine/core"; import { IconBrandDocker, IconExternalLink, IconServer } from "@tabler/icons-react"; import combineClasses from "clsx"; import { clientApi } from "@homarr/api/client"; import { useRequiredBoard } from "@homarr/boards/context"; import { getIconUrl } from "@homarr/definitions"; import type { WudContainerUpdate } from "@homarr/integrations"; import { useScopedI18n } from "@homarr/translation/client"; import { WidgetEmptyState } from "../common/empty-state"; import type { WidgetComponentProps } from "../definition"; import classes from "./component.module.css"; export default function WudWidget({ integrationIds, options, width }: WidgetComponentProps<"wud">) { const integrationId = integrationIds[0]; if (!integrationId) return null; return ; } const WudWidgetContent = ({ integrationId, options, width, }: { integrationId: string; options: WidgetComponentProps<"wud">["options"]; width: number; }) => { const t = useScopedI18n("widget.wud"); const [data] = clientApi.widget.wud.getStats.useSuspenseQuery({ integrationId }); const board = useRequiredBoard(); const isTiny = width < 256; const stats = data.stats; if (stats.totalContainers === 0) return ; const updatePercentage = Math.round((stats.updatesAvailable / stats.totalContainers) * 100); const badgeColor = stats.updatesAvailable === 0 ? "green" : progressColor(updatePercentage); const showUpdateList = options.showUpdateList && stats.updates.length > 0; const ring = ( {updatePercentage}% } sections={[{ value: updatePercentage, color: progressColor(updatePercentage) }]} /> ); const summary = ( {stats.totalContainers} {t("monitored")} {t("updatesAvailable", { count: stats.updatesAvailable })} ); const tinyContent = options.showRing ? ( {stats.totalContainers} {t("monitored")} {t("updatesAvailable", { count: stats.updatesAvailable })} } events={{ hover: true, focus: true, touch: false }} >
{ring}
) : (
{t("updatesAvailable", { count: stats.updatesAvailable })}
); return ( {options.showTitle && !isTiny && ( {t("title")} {stats.updatesAvailable > 0 && ( {stats.updatesAvailable} )} )} {isTiny ? ( tinyContent ) : options.layout === "horizontal" ? ( {options.showRing && ring} {summary} ) : ( {options.showRing && ring} {summary} )} {showUpdateList && ( {stats.updates.map((update) => ( ))} )} ); }; const progressColor = (percentage: number) => { if (percentage < 40) return "green"; else if (percentage < 60) return "yellow"; else if (percentage < 90) return "orange"; else return "red"; }; const SHA256_DIGEST_PREFIX = "sha256:"; const SHA256_DIGEST_LENGTH = SHA256_DIGEST_PREFIX.length + 64; const GENERIC_MAX_LENGTH = 20; const GENERIC_DISPLAY_LENGTH = 17; const isDigestVersion = (value: string | null): boolean => value !== null && value.startsWith(SHA256_DIGEST_PREFIX) && value.length === SHA256_DIGEST_LENGTH; const truncateVersion = (value: string | null): string | null => { if (!value) return value; if (value.length > GENERIC_MAX_LENGTH) { return `${value.slice(0, GENERIC_DISPLAY_LENGTH)}…`; } return value; }; const buildVersionText = (currentVersion: string | null, newVersion: string | null) => currentVersion && newVersion ? `${currentVersion} → ${newVersion}` : newVersion; const UpdateCard = ({ update, radius, className, }: { update: WudContainerUpdate; radius: string | undefined; className: string | undefined; }) => { const t = useScopedI18n("widget.wud"); const isDigestUpdate = isDigestVersion(update.newVersion); const fullVersionText = buildVersionText(update.currentVersion, update.newVersion); const versionText = isDigestUpdate ? t("updateAvailable") : buildVersionText(truncateVersion(update.currentVersion), truncateVersion(update.newVersion)); return ( {update.name} {versionText && ( {versionText} )} {update.link && ( )} ); };