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:
2026-03-23 21:50:10 -07:00
parent 3806264343
commit d6616c5859
63 changed files with 3111 additions and 1130 deletions
@@ -1,5 +1,6 @@
"use client";
import { useQuery } from "@tanstack/react-query";
import { Button } from "@workspace/ui/components/button";
import {
Dialog,
@@ -18,12 +19,14 @@ import {
SelectTrigger,
SelectValue,
} from "@workspace/ui/components/select";
import {
bindNodeGroups,
getNodeGroupList,
} from "@workspace/ui/services/admin/group";
import { Loader2 } from "lucide-react";
import { useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import { useQuery } from "@tanstack/react-query";
import { toast } from "sonner";
import { getNodeGroupList, bindNodeGroups } from "@workspace/ui/services/admin/group";
interface BindNodeGroupsDialogProps {
userGroupIds: number[];
@@ -41,7 +44,9 @@ export default function BindNodeGroupsDialog({
const { t } = useTranslation("group");
const [open, setOpen] = useState(false);
const [saving, setSaving] = useState(false);
const [selectedNodeGroupId, setSelectedNodeGroupId] = useState<number | undefined>();
const [selectedNodeGroupId, setSelectedNodeGroupId] = useState<
number | undefined
>();
const { data: nodeGroupsData, isLoading } = useQuery({
queryKey: ["nodeGroups"],
@@ -78,10 +83,10 @@ export default function BindNodeGroupsDialog({
} as API.BindNodeGroupsRequest);
toast.success(
t("bindSuccess", "Successfully bound {{userGroupCount}} user groups to node group").replace(
/{{userGroupCount}}/g,
String(userGroupIds.length)
)
t(
"bindSuccess",
"Successfully bound {{userGroupCount}} user groups to node group"
).replace(/{{userGroupCount}}/g, String(userGroupIds.length))
);
setOpen(false);
@@ -101,12 +106,15 @@ export default function BindNodeGroupsDialog({
: userGroupNames.join(", ");
return (
<Dialog open={open} onOpenChange={(newOpen) => {
setOpen(newOpen);
onOpenChange?.(newOpen);
}}>
<Dialog
onOpenChange={(newOpen) => {
setOpen(newOpen);
onOpenChange?.(newOpen);
}}
open={open}
>
<DialogTrigger asChild>
<Button variant="outline" size="sm">
<Button size="sm" variant="outline">
{t("bindNodeGroup", "Bind Node Group")}
</Button>
</DialogTrigger>
@@ -129,18 +137,25 @@ export default function BindNodeGroupsDialog({
</div>
) : (
<div className="space-y-2">
<Label htmlFor="node-group">{t("selectNodeGroup", "Select Node Group")}</Label>
<Label htmlFor="node-group">
{t("selectNodeGroup", "Select Node Group")}
</Label>
<Select
onValueChange={(val) =>
setSelectedNodeGroupId(Number.parseInt(val, 10) || undefined)
}
value={selectedNodeGroupId?.toString() || ""}
onValueChange={(val) => setSelectedNodeGroupId(parseInt(val) || undefined)}
>
<SelectTrigger id="node-group" className="w-full">
<SelectValue placeholder={t("selectNodeGroupPlaceholder", "Select a node group...")} />
<SelectTrigger className="w-full" id="node-group">
<SelectValue
placeholder={t(
"selectNodeGroupPlaceholder",
"Select a node group..."
)}
/>
</SelectTrigger>
<SelectContent>
<SelectItem value="0">
{t("unbound", "Unbound")}
</SelectItem>
<SelectItem value="0">{t("unbound", "Unbound")}</SelectItem>
{nodeGroupsData?.map((nodeGroup) => (
<SelectItem key={nodeGroup.id} value={String(nodeGroup.id)}>
{nodeGroup.name}
@@ -154,16 +169,19 @@ export default function BindNodeGroupsDialog({
<DialogFooter>
<Button
variant="outline"
disabled={saving}
onClick={() => {
setOpen(false);
onOpenChange?.(false);
}}
disabled={saving}
variant="outline"
>
{t("cancel", "Cancel")}
</Button>
<Button onClick={handleBind} disabled={saving || selectedNodeGroupId === undefined}>
<Button
disabled={saving || selectedNodeGroupId === undefined}
onClick={handleBind}
>
{saving && <Loader2 className="mr-2 h-4 w-4 animate-spin" />}
{t("confirm", "Confirm")}
</Button>