Spaces:
Sleeping
Sleeping
File size: 5,610 Bytes
5cdde73 3193174 5cdde73 3193174 81f5c1c 3193174 5cdde73 3193174 5cdde73 3193174 81f5c1c 5cdde73 81f5c1c 5cdde73 3193174 5cdde73 81f5c1c 5cdde73 3193174 5cdde73 3193174 5cdde73 3193174 5cdde73 3193174 5cdde73 3193174 5cdde73 81f5c1c 5cdde73 3193174 5cdde73 3193174 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 | 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>
);
}
|