feat: refine widget interactions

This commit is contained in:
Thomas Camlong authored and ajnart committed 2026-08-14 11:07:08 +02:00
1 parent a8a7545f40
commit 842e86012d
107 files changed
+3144 -1233

No files matched your search

+46 -24
View File
@@ -50,12 +50,15 @@ export const hasTotalFirewallFailure = (queries: readonly FirewallQueryState[])
export default function FirewallWidget({
integrationIds,
width,
height,
itemId,
displayMode,
}: WidgetComponentProps<"firewall">) {
const [selectedFirewall, setSelectedFirewall] = useState("");
const isAdvanced = displayMode === "advanced";
const isTiny = !isAdvanced && width < 256;
const isTiny = !isAdvanced && (width < 256 || height < 180);
const ringSize = isAdvanced ? 100 : height < 120 ? 44 : isTiny ? 64 : 100;
const showInterfaces = isAdvanced || height >= 120;
const t = useI18n();
const handleSelect = useCallback((value: string | null) => {
@@ -102,6 +105,17 @@ export default function FirewallWidget({
value: firewallId,
}));
if (firewallIds.length === 0) {
const isLoading = queries.some((query) => query.isPending || query.isFetching);
return (
<Center h="100%" p="sm">
<Text size="sm" c="dimmed" ta="center">
{isLoading ? t("common.action.loading") : t("widget.firewall.empty.noInterfaces")}
</Text>
</Center>
);
}
return (
<ScrollArea h="100%">
<Stack gap="xs" p={isAdvanced ? "xs" : 0}>
@@ -146,6 +160,8 @@ export default function FirewallWidget({
hasError={hasError}
isAdvanced={isAdvanced}
isTiny={isTiny}
ringSize={ringSize}
showInterfaces={showInterfaces}
accordionValue={accordionValue}
setAccordionValue={setAccordionValue}
errorLabel={t("widget.firewall.error.internalServerError")}
@@ -175,6 +191,8 @@ interface FirewallPanelProps {
hasError: boolean;
isAdvanced: boolean;
isTiny: boolean;
ringSize: number;
showInterfaces: boolean;
accordionValue: string | null;
setAccordionValue: (value: string | null) => void;
errorLabel: string;
@@ -197,6 +215,8 @@ const FirewallPanel = ({
hasError,
isAdvanced,
isTiny,
ringSize,
showInterfaces,
accordionValue,
setAccordionValue,
errorLabel,
@@ -237,33 +257,35 @@ const FirewallPanel = ({
<Flex justify="center" align="center" wrap="wrap">
{cpu !== undefined && (
<MetricRing value={cpu} icon={IconCpu} isTiny={isTiny} label={t("widget.firewall.widget.cpu")} t={t} />
<MetricRing value={cpu} icon={IconCpu} size={ringSize} label={t("widget.firewall.widget.cpu")} t={t} />
)}
{memory !== undefined && (
<MetricRing
value={memory}
icon={IconBrain}
isTiny={isTiny}
size={ringSize}
label={t("widget.firewall.widget.memory")}
t={t}
/>
)}
</Flex>
<InterfacesPanel
summary={interfaces ?? []}
hasResult={interfacesLoaded}
hasError={interfacesError}
isAdvanced={isAdvanced}
isTiny={isTiny}
accordionValue={accordionValue}
setAccordionValue={setAccordionValue}
errorLabel={errorLabel}
errorBadgeLabel={errorBadgeLabel}
noDataLabel={noDataLabel}
loadingLabel={loadingLabel}
label={interfacesLabel}
/>
{showInterfaces && (
<InterfacesPanel
summary={interfaces ?? []}
hasResult={interfacesLoaded}
hasError={interfacesError}
isAdvanced={isAdvanced}
isTiny={isTiny}
accordionValue={accordionValue}
setAccordionValue={setAccordionValue}
errorLabel={errorLabel}
errorBadgeLabel={errorBadgeLabel}
noDataLabel={noDataLabel}
loadingLabel={loadingLabel}
label={interfacesLabel}
/>
)}
</Stack>
</Paper>
);
@@ -271,12 +293,12 @@ const FirewallPanel = ({
interface MetricRingProps {
value: number;
icon: typeof IconCpu;
isTiny: boolean;
size: number;
label: string;
t: TranslationFunction;
}
const MetricRing = ({ value, icon: Icon, isTiny, label, t }: MetricRingProps) => {
const MetricRing = ({ value, icon: Icon, size, label, t }: MetricRingProps) => {
const safeValue = Math.max(0, Math.min(100, Number.isFinite(value) ? value : 0));
const status = getMetricStatus(safeValue);
const statusLabel = t(`widget.firewall.status.${status}`);
@@ -289,13 +311,13 @@ const MetricRing = ({ value, icon: Icon, isTiny, label, t }: MetricRingProps) =>
status: statusLabel,
})}
roundCaps
size={isTiny ? 50 : 100}
thickness={isTiny ? 4 : 8}
size={size}
thickness={size < 72 ? 4 : 8}
label={
<Center style={{ flexDirection: "column" }}>
<Text size={isTiny ? "8px" : "xs"}>{safeValue.toFixed(1)}%</Text>
<Icon size={isTiny ? 8 : 16} />
{!isTiny && <Text size="8px">{statusLabel}</Text>}
<Text size={size < 72 ? "8px" : "xs"}>{safeValue.toFixed(1)}%</Text>
<Icon size={size < 72 ? 8 : 16} />
{size >= 72 && <Text size="8px">{statusLabel}</Text>}
</Center>
}
sections={[
@@ -10,16 +10,18 @@ interface FirewallMenuProps {
}
export const FirewallMenu = ({ onChange, isTiny, dropdownItems, selectedFirewall }: FirewallMenuProps) => (
<Box>
<Box style={{ flex: 1, minWidth: 0 }}>
<Select
value={selectedFirewall}
onChange={onChange}
size={isTiny ? "8px" : "xs"}
size="xs"
w="100%"
color="lightgray"
data={dropdownItems}
styles={{
input: {
minHeight: "24px",
paddingInline: isTiny ? 6 : undefined,
},
}}
/>
@@ -29,8 +29,9 @@ export const FirewallVersion = ({ firewallsVersionData, selectedFirewall, isTiny
autoContrast
variant="outline"
color="lightgray"
size={isTiny ? "8px" : "xs"}
style={{ minHeight: "24px" }}
size="xs"
style={{ minHeight: "24px", maxWidth: isTiny ? 72 : undefined }}
styles={{ label: { overflow: "hidden", textOverflow: "ellipsis" } }}
>
{formatVersion(version ?? "", t("widget.firewall.versionUnknown"))}
</Badge>