merge: 同步 upstream/main 新功能到定制版本
- feat: Add slider verification code (bd67997) - fix bug: Inventory cannot be zero (1f7a6ee) - fix: resolve merge conflicts and lint errors
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { Badge } from "@workspace/ui/components/badge";
|
||||
import { Button } from "@workspace/ui/components/button";
|
||||
import { Switch } from "@workspace/ui/components/switch";
|
||||
@@ -8,6 +9,10 @@ import {
|
||||
ProTable,
|
||||
type ProTableActions,
|
||||
} from "@workspace/ui/composed/pro-table/pro-table";
|
||||
import {
|
||||
getGroupConfig,
|
||||
getNodeGroupList,
|
||||
} from "@workspace/ui/services/admin/group";
|
||||
import {
|
||||
createNode,
|
||||
deleteNode,
|
||||
@@ -16,8 +21,6 @@ import {
|
||||
toggleNodeStatus,
|
||||
updateNode,
|
||||
} from "@workspace/ui/services/admin/server";
|
||||
import { getGroupConfig, getNodeGroupList } from "@workspace/ui/services/admin/group";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { useMemo, useRef, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { toast } from "sonner";
|
||||
@@ -52,7 +55,7 @@ export default function Nodes() {
|
||||
},
|
||||
});
|
||||
|
||||
const isGroupEnabled = groupConfigData?.enabled || false;
|
||||
const isGroupEnabled = groupConfigData?.enabled;
|
||||
|
||||
// Dynamic columns based on group feature status
|
||||
const columns = useMemo(() => {
|
||||
@@ -121,13 +124,13 @@ export default function Nodes() {
|
||||
id: "node_group_ids",
|
||||
header: t("nodeGroups", "Node Groups"),
|
||||
cell: ({ row }: { row: any }) => {
|
||||
const groupIds = row.original.node_group_ids as number[] || [];
|
||||
const groupIds = (row.original.node_group_ids as number[]) || [];
|
||||
|
||||
// Public node indicator (when node_group_ids is empty)
|
||||
if (groupIds.length === 0) {
|
||||
return (
|
||||
<div className="flex items-center gap-2">
|
||||
<Badge variant="secondary" className="text-xs">
|
||||
<Badge className="text-xs" variant="secondary">
|
||||
{t("public", "Public")}
|
||||
</Badge>
|
||||
</div>
|
||||
@@ -151,7 +154,14 @@ export default function Nodes() {
|
||||
}
|
||||
|
||||
return baseColumns;
|
||||
}, [isGroupEnabled, nodeGroupsData, t, getServerName, getServerAddress, getProtocolPort]);
|
||||
}, [
|
||||
isGroupEnabled,
|
||||
nodeGroupsData,
|
||||
t,
|
||||
getServerName,
|
||||
getServerAddress,
|
||||
getProtocolPort,
|
||||
]);
|
||||
|
||||
return (
|
||||
<ProTable<API.Node, { search: string; node_group_id?: number }>
|
||||
@@ -168,7 +178,10 @@ export default function Nodes() {
|
||||
const body: API.UpdateNodeRequest = {
|
||||
...row,
|
||||
...values,
|
||||
node_group_ids: values.node_group_ids?.map((id: string | number) => Number(id)) || [],
|
||||
node_group_ids:
|
||||
values.node_group_ids?.map((id: string | number) =>
|
||||
Number(id)
|
||||
) || [],
|
||||
} as any;
|
||||
await updateNode(body);
|
||||
toast.success(t("updated", "Updated"));
|
||||
@@ -274,9 +287,10 @@ export default function Nodes() {
|
||||
port: Number(values.port!),
|
||||
tags: values.tags || [],
|
||||
};
|
||||
// Add node_group_ids if it exists
|
||||
if (values.node_group_ids) {
|
||||
body.node_group_ids = values.node_group_ids.map((id: string | number) => Number(id));
|
||||
body.node_group_ids = values.node_group_ids.map(
|
||||
(id: string | number) => Number(id)
|
||||
);
|
||||
}
|
||||
await createNode(body);
|
||||
toast.success(t("created", "Created"));
|
||||
@@ -290,8 +304,8 @@ export default function Nodes() {
|
||||
return false;
|
||||
}
|
||||
}}
|
||||
title={t("drawerCreateTitle", "Create Node")}
|
||||
trigger={t("create", "Create")}
|
||||
title={t("drawerCreateTitle", "Create Landing Node")}
|
||||
trigger={t("create", "Create Landing Node")}
|
||||
/>
|
||||
),
|
||||
}}
|
||||
@@ -372,7 +386,9 @@ export default function Nodes() {
|
||||
page: pagination.page,
|
||||
size: pagination.size,
|
||||
search: filter?.search || undefined,
|
||||
node_group_id: filter?.node_group_id ? Number(filter.node_group_id) : undefined,
|
||||
node_group_id: filter?.node_group_id
|
||||
? Number(filter.node_group_id)
|
||||
: undefined,
|
||||
};
|
||||
|
||||
const { data } = await filterNodeList(filters);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { Button } from "@workspace/ui/components/button";
|
||||
import { Checkbox } from "@workspace/ui/components/checkbox";
|
||||
import {
|
||||
@@ -25,8 +26,10 @@ import {
|
||||
import { Combobox } from "@workspace/ui/composed/combobox";
|
||||
import { EnhancedInput } from "@workspace/ui/composed/enhanced-input";
|
||||
import TagInput from "@workspace/ui/composed/tag-input";
|
||||
import { getGroupConfig, getNodeGroupList } from "@workspace/ui/services/admin/group";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import {
|
||||
getGroupConfig,
|
||||
getNodeGroupList,
|
||||
} from "@workspace/ui/services/admin/group";
|
||||
import type { TFunction } from "i18next";
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import { useForm } from "react-hook-form";
|
||||
@@ -150,7 +153,7 @@ export default function NodeForm(props: {
|
||||
},
|
||||
});
|
||||
|
||||
const isGroupEnabled = groupConfigData?.enabled || false;
|
||||
const isGroupEnabled = groupConfigData?.enabled;
|
||||
|
||||
useEffect(() => {
|
||||
if (initialValues) {
|
||||
@@ -166,15 +169,21 @@ export default function NodeForm(props: {
|
||||
|
||||
// Copy only the values we need from initialValues
|
||||
if (initialValues.name) resetValues.name = initialValues.name;
|
||||
if (initialValues.server_id) resetValues.server_id = initialValues.server_id;
|
||||
if (initialValues.server_id)
|
||||
resetValues.server_id = initialValues.server_id;
|
||||
if (initialValues.protocol) resetValues.protocol = initialValues.protocol;
|
||||
if (initialValues.address) resetValues.address = initialValues.address;
|
||||
if (initialValues.port) resetValues.port = initialValues.port;
|
||||
if (initialValues.tags) resetValues.tags = initialValues.tags;
|
||||
|
||||
// Convert node_group_ids from number[] to string[], ensure it's always an array
|
||||
if (initialValues.node_group_ids && Array.isArray(initialValues.node_group_ids)) {
|
||||
resetValues.node_group_ids = initialValues.node_group_ids.map((id: string | number) => String(id));
|
||||
if (
|
||||
initialValues.node_group_ids &&
|
||||
Array.isArray(initialValues.node_group_ids)
|
||||
) {
|
||||
resetValues.node_group_ids = initialValues.node_group_ids.map(
|
||||
(id: string | number) => String(id)
|
||||
);
|
||||
} else {
|
||||
resetValues.node_group_ids = [];
|
||||
}
|
||||
@@ -449,23 +458,32 @@ export default function NodeForm(props: {
|
||||
<div className="grid grid-cols-2 gap-2">
|
||||
{nodeGroupsData?.map((g) => (
|
||||
<div
|
||||
key={g.id}
|
||||
className="flex items-center space-x-2"
|
||||
key={g.id}
|
||||
>
|
||||
<Checkbox
|
||||
checked={field.value?.includes(String(g.id))}
|
||||
id={`node-group-${g.id}`}
|
||||
checked={field.value?.includes(String(g.id)) || false}
|
||||
onCheckedChange={(checked) => {
|
||||
// Ensure field.value is always an array
|
||||
const currentValue = Array.isArray(field.value) ? field.value : [];
|
||||
const currentValue = Array.isArray(
|
||||
field.value
|
||||
)
|
||||
? field.value
|
||||
: [];
|
||||
if (checked) {
|
||||
const newValue = [...currentValue, String(g.id)];
|
||||
const newValue = [
|
||||
...currentValue,
|
||||
String(g.id),
|
||||
];
|
||||
form.setValue(field.name, newValue, {
|
||||
shouldValidate: true,
|
||||
shouldDirty: true,
|
||||
});
|
||||
} else {
|
||||
const newValue = currentValue.filter((v: string) => v !== String(g.id));
|
||||
const newValue = currentValue.filter(
|
||||
(v: string) => v !== String(g.id)
|
||||
);
|
||||
form.setValue(field.name, newValue, {
|
||||
shouldValidate: true,
|
||||
shouldDirty: true,
|
||||
@@ -474,8 +492,8 @@ export default function NodeForm(props: {
|
||||
}}
|
||||
/>
|
||||
<Label
|
||||
htmlFor={`node-group-${g.id}`}
|
||||
className="cursor-pointer"
|
||||
htmlFor={`node-group-${g.id}`}
|
||||
>
|
||||
{g.name}
|
||||
</Label>
|
||||
|
||||
Reference in New Issue
Block a user