fix: support per-server max/from/to in cycle transfer stats (#64)

* Initial plan

* fix: support per-server max/from/to values in cycle transfer stats

When the Nezha backend has multiple transfer rules within one alert rule,
the API may return per-server max values (as a map) instead of a single
max value. Update the TypeScript interface and CycleTransferStats component
to handle both formats:
- Single value (number/string): used for all servers (current behavior)
- Per-server map ({[serverId]: value}): uses the correct value per server

Fixes #63

Agent-Logs-Url: https://github.com/hamster1963/nezha-dash-v2/sessions/88f98dad-c2eb-4728-ac98-e02a95d5144d

Co-authored-by: hamster1963 <71394853+hamster1963@users.noreply.github.com>

* fix: update schema version in biome.json and improve null checks in components

* chore: auto-fix linting and formatting issues

* fix: guard null/zero in CycleTransferStats per-server map lookups

Agent-Logs-Url: https://github.com/hamster1963/nezha-dash-v2/sessions/2fb2306b-8011-487d-b284-9768778db8c3

Co-authored-by: hamster1963 <71394853+hamster1963@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: hamster1963 <71394853+hamster1963@users.noreply.github.com>
Co-authored-by: hamster1963 <1410514192@qq.com>
This commit is contained in:
Copilot
2026-05-26 01:34:02 +08:00
committed by GitHub
co-authored by hamster1963 hamster1963
parent f69f480d3b
commit 2d46fb6b86
18 changed files with 308 additions and 401 deletions
+26 -3
View File
@@ -40,13 +40,36 @@ export const CycleTransferStatsCard: React.FC<CycleTransferStatsProps> = ({
return null;
}
// Support per-server max values (map) or single max value (number)
const max =
typeof cycleData.max === "object" && cycleData.max !== null
? cycleData.max[serverId]
: cycleData.max;
// Support per-server from values (map) or single from value (string)
const from =
typeof cycleData.from === "object" && cycleData.from !== null
? cycleData.from[serverId]
: cycleData.from;
// Support per-server to values (map) or single to value (string)
const to =
typeof cycleData.to === "object" && cycleData.to !== null
? cycleData.to[serverId]
: cycleData.to;
// Skip rendering when required fields are missing or would cause invalid display
if (!max || !from || !to) {
return null;
}
return (
<CycleTransferStatsClient
key={`${cycleId}-${serverId}`}
name={cycleData.name}
from={cycleData.from}
to={cycleData.to}
max={cycleData.max}
from={from}
to={to}
max={max}
serverStats={[
{
serverId,
+1 -1
View File
@@ -5,7 +5,7 @@ export default function PlanInfo({
}: {
parsedData: PublicNoteData;
}) {
if (!parsedData || !parsedData.planDataMod) {
if (!parsedData?.planDataMod) {
return null;
}
+1 -1
View File
@@ -36,7 +36,7 @@ export default function ServerFlag({
};
checkEmojiSupport();
}, [forceUseSvgFlag]);
}, []);
if (!country_code) return null;
+1 -1
View File
@@ -13,7 +13,7 @@ export default function BillingInfo({
parsedData: PublicNoteData;
}) {
const { t } = useTranslation();
if (!parsedData || !parsedData.billingDataMod) {
if (!parsedData?.billingDataMod) {
return null;
}
+1 -1
View File
@@ -52,4 +52,4 @@ const AccordionContent = React.forwardRef<
AccordionContent.displayName = AccordionPrimitive.Content.displayName;
export { Accordion, AccordionItem, AccordionTrigger, AccordionContent };
export { Accordion, AccordionContent, AccordionItem, AccordionTrigger };
+4 -4
View File
@@ -77,9 +77,9 @@ CardFooter.displayName = "CardFooter";
export {
Card,
CardHeader,
CardFooter,
CardTitle,
CardDescription,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
};
+2 -2
View File
@@ -389,9 +389,9 @@ function getPayloadConfigFromPayload(
export {
ChartContainer,
ChartTooltip,
ChartTooltipContent,
ChartLegend,
ChartLegendContent,
ChartStyle,
ChartTooltip,
ChartTooltipContent,
};
+3 -3
View File
@@ -149,11 +149,11 @@ CommandShortcut.displayName = "CommandShortcut";
export {
Command,
CommandDialog,
CommandInput,
CommandList,
CommandEmpty,
CommandGroup,
CommandInput,
CommandItem,
CommandShortcut,
CommandList,
CommandSeparator,
CommandShortcut,
};
+6 -6
View File
@@ -108,13 +108,13 @@ DialogDescription.displayName = DialogPrimitive.Description.displayName;
export {
Dialog,
DialogPortal,
DialogOverlay,
DialogClose,
DialogTrigger,
DialogContent,
DialogHeader,
DialogFooter,
DialogTitle,
DialogDescription,
DialogFooter,
DialogHeader,
DialogOverlay,
DialogPortal,
DialogTitle,
DialogTrigger,
};
+7 -7
View File
@@ -180,18 +180,18 @@ DropdownMenuShortcut.displayName = "DropdownMenuShortcut";
export {
DropdownMenu,
DropdownMenuTrigger,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuCheckboxItem,
DropdownMenuRadioItem,
DropdownMenuContent,
DropdownMenuGroup,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuPortal,
DropdownMenuRadioGroup,
DropdownMenuRadioItem,
DropdownMenuSeparator,
DropdownMenuShortcut,
DropdownMenuGroup,
DropdownMenuPortal,
DropdownMenuSub,
DropdownMenuSubContent,
DropdownMenuSubTrigger,
DropdownMenuRadioGroup,
DropdownMenuTrigger,
};
+1 -1
View File
@@ -25,4 +25,4 @@ const PopoverContent = React.forwardRef<
));
PopoverContent.displayName = PopoverPrimitive.Content.displayName;
export { Popover, PopoverTrigger, PopoverContent };
export { Popover, PopoverContent, PopoverTrigger };
+6 -6
View File
@@ -145,13 +145,13 @@ SelectSeparator.displayName = SelectPrimitive.Separator.displayName;
export {
Select,
SelectGroup,
SelectValue,
SelectTrigger,
SelectContent,
SelectLabel,
SelectGroup,
SelectItem,
SelectSeparator,
SelectScrollUpButton,
SelectLabel,
SelectScrollDownButton,
SelectScrollUpButton,
SelectSeparator,
SelectTrigger,
SelectValue,
};
+3 -3
View File
@@ -106,11 +106,11 @@ TableCaption.displayName = "TableCaption";
export {
Table,
TableHeader,
TableBody,
TableCaption,
TableCell,
TableFooter,
TableHead,
TableHeader,
TableRow,
TableCell,
TableCaption,
};
+1 -1
View File
@@ -24,4 +24,4 @@ const TooltipContent = React.forwardRef<
));
TooltipContent.displayName = TooltipPrimitive.Content.displayName;
export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider };
export { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger };
+4 -4
View File
@@ -128,10 +128,10 @@ export interface CycleTransferStats {
export interface CycleTransferData {
name: string;
from: string;
to: string;
max: number;
min: number;
from: string | { [key: string]: string };
to: string | { [key: string]: string };
max: number | { [key: string]: number };
min: number | { [key: string]: number };
server_name: {
[key: string]: string;
};