gMAS / web_ui /frontend /src /components /graph /EdgeConditionDialog.tsx
Артём Боярских
fix: fixed some bugs
81f5c1c
import { useState, useEffect } from "react";
import { Button } from "@/components/ui/button";
import { Label } from "@/components/ui/label";
import {
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
DialogFooter,
DialogDescription,
} from "@/components/ui/dialog";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import { Input } from "@/components/ui/input";
const BUILTIN_CONDITIONS = [
{ value: "none", label: "None (always)" },
{ value: "always", label: "Always" },
{ value: "source_success", label: "Source Succeeded" },
{ value: "source_failed", label: "Source Failed" },
{ value: "has_response", label: "Has Response" },
{ value: "contains", label: "Contains Keyword..." },
{ value: "custom", label: "Custom..." },
];
interface EdgeConditionDialogProps {
open: boolean;
onOpenChange: (open: boolean) => void;
edgeId: string;
currentCondition?: string | null;
currentWeight?: number;
onSave: (edgeId: string, condition: string, weight: number) => void;
}
export function EdgeConditionDialog({
open,
onOpenChange,
edgeId,
currentCondition,
currentWeight = 1.0,
onSave,
}: EdgeConditionDialogProps) {
// Parse existing condition to detect contains:xxx format
const parseCondition = (cond: string | null | undefined) => {
if (!cond) return { selected: "none", keyword: "", custom: "" };
if (cond.startsWith("contains:")) {
return { selected: "contains", keyword: cond.slice("contains:".length), custom: "" };
}
const builtin = BUILTIN_CONDITIONS.find((c) => c.value === cond);
if (builtin) return { selected: cond, keyword: "", custom: "" };
return { selected: "custom", keyword: "", custom: cond };
};
const parsed = parseCondition(currentCondition);
const [selected, setSelected] = useState(parsed.selected);
const [keyword, setKeyword] = useState(parsed.keyword);
const [customCondition, setCustomCondition] = useState(parsed.custom);
const [weight, setWeight] = useState(currentWeight);
// Re-sync when the dialog opens with new props
useEffect(() => {
if (open) {
const p = parseCondition(currentCondition);
setSelected(p.selected);
setKeyword(p.keyword);
setCustomCondition(p.custom);
setWeight(currentWeight);
}
}, [open, currentCondition, currentWeight]);
const handleSave = () => {
let condition = "";
if (selected === "custom") {
condition = customCondition;
} else if (selected === "contains") {
condition = keyword ? `contains:${keyword}` : "";
} else if (selected === "none") {
condition = "";
} else {
condition = selected;
}
onSave(edgeId, condition, weight);
onOpenChange(false);
};
return (
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent className="sm:max-w-[420px]">
<DialogHeader>
<DialogTitle>Edge Configuration</DialogTitle>
<DialogDescription>Set condition and weight for this edge transition.</DialogDescription>
</DialogHeader>
<div className="space-y-4 py-4">
{/* Condition */}
<div className="space-y-2">
<Label>Condition Type</Label>
<Select value={selected} onValueChange={setSelected}>
<SelectTrigger>
<SelectValue placeholder="Select condition..." />
</SelectTrigger>
<SelectContent>
{BUILTIN_CONDITIONS.map((c) => (
<SelectItem key={c.value} value={c.value}>
{c.label}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
{selected === "contains" && (
<div className="space-y-2">
<Label>Keyword</Label>
<Input
placeholder="e.g. APPROVED"
value={keyword}
onChange={(e) => setKeyword(e.target.value)}
/>
<p className="text-[10px] text-muted-foreground">
Edge is taken only if the source agent's response contains this keyword.
</p>
</div>
)}
{selected === "custom" && (
<div className="space-y-2">
<Label>Custom Condition Name</Label>
<Input
placeholder="my_condition"
value={customCondition}
onChange={(e) => setCustomCondition(e.target.value)}
/>
</div>
)}
{/* Weight */}
<div className="space-y-2">
<div className="flex items-center justify-between">
<Label>Edge Weight</Label>
<span className="text-xs text-muted-foreground font-mono">{weight.toFixed(2)}</span>
</div>
<input
type="range"
min={0}
max={1}
step={0.05}
value={weight}
onChange={(e) => setWeight(parseFloat(e.target.value))}
className="w-full accent-primary h-1.5"
/>
<p className="text-[10px] text-muted-foreground">
Higher weight = higher priority when scheduling. Edges below 0.1 may be pruned.
</p>
</div>
</div>
<DialogFooter>
<Button variant="outline" onClick={() => onOpenChange(false)}>
Cancel
</Button>
<Button onClick={handleSave}>Save</Button>
</DialogFooter>
</DialogContent>
</Dialog>
);
}