@datatechsolutions/ui 2.11.76 → 2.11.78

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,13 +1,13 @@
1
1
  "use client";
2
- import { Workspace, useModalStore, CATEGORY_COLORS, CATEGORY_PILL_COLORS, ICON_MAP, WorkflowCanvas, getEntityIcon, getEntityGradient, useWorkflowStore, LOGIC_ICON_MAP, LOGIC_NODE_GRADIENTS, getFrameworkMeta, getCompatibleModels, isModelCompatibleWithFramework, FRAMEWORK_META, isFrameworkCompatibleWithProviders } from '../chunk-JIQSAUYC.mjs';
3
- export { AgentFlowNode, AgentToolFlowNode, AnswerFlowNode, AnthropicIcon, CodeFlowNode, CrewAIIcon, DocumentExtractorFlowNode, EndFlowNode, EntityFlowNode, FRAMEWORK_META, GoogleADKIcon, GroupFlowNode, HttpRequestFlowNode, IfElseFlowNode, IterationFlowNode, IterationStartFlowNode, KnowledgeBaseFlowNode, LOGIC_ICON_MAP, LOGIC_NODE_BADGE_COLORS, LOGIC_NODE_GRADIENTS, LOGIC_NODE_HANDLE_COLORS, LangChainIcon, ListOperatorFlowNode, LogicNodeModal, MINIMAP_NODE_COLORS, ModelProviderFlowNode, NODE_EXECUTION_ACCENT_COLORS, NodeCard, NodeContextMenu, NoteFlowNode, OpenAIIcon, PanelContextMenu, ParameterExtractorFlowNode, QuestionClassifierFlowNode, RuleFlowNode, SelectionContextMenu, StartFlowNode, StrandsIcon, TemplateTransformFlowNode, ToolFlowNode, VariableAggregatorFlowNode, VariableAssignerFlowNode, WorkflowBuilderProvider, Workspace, getCompatibleModels, getDefaultFrameworkForModel, getEntityBadgeColor, getEntityGradient, getEntityHandleColor, getEntityIcon, getEntityMinimapColor, getFrameworkMeta, getNodeExecutionAccent, getNodeExecutionAccentRgb, isModelCompatibleWithFramework, useModalStore, useWorkflowBuilderClient, useWorkflowBuilderClientOptional, useWorkflowStore } from '../chunk-JIQSAUYC.mjs';
4
- import { GlassModal, Button, FormInput, FormTextarea, ToggleSwitch, Input, DynamicIslandConfirm } from '../chunk-ZJQ5RLGK.mjs';
5
- import '../chunk-D2JF6C3E.mjs';
2
+ import '../chunk-JB6RNAD2.mjs';
3
+ export { topologicalSortAgents, validateGraphNodeConfigs, validateNodeConfig, validateWorkflowGraph } from '../chunk-53SRKVKQ.mjs';
4
+ import { Workspace, useModalStore, CATEGORY_COLORS, CATEGORY_PILL_COLORS, ICON_MAP, WorkflowCanvas, getEntityIcon, getEntityGradient, useWorkflowStore, LOGIC_ICON_MAP, LOGIC_NODE_GRADIENTS, getFrameworkMeta, getCompatibleModels, isModelCompatibleWithFramework, FRAMEWORK_META, isFrameworkCompatibleWithProviders } from '../chunk-3VHSRL3N.mjs';
5
+ export { AgentFlowNode, AgentToolFlowNode, AnswerFlowNode, AnthropicIcon, CodeFlowNode, CrewAIIcon, DocumentExtractorFlowNode, EndFlowNode, EntityFlowNode, FRAMEWORK_META, GoogleADKIcon, GroupFlowNode, HttpRequestFlowNode, IfElseFlowNode, IterationFlowNode, IterationStartFlowNode, KnowledgeBaseFlowNode, LOGIC_ICON_MAP, LOGIC_NODE_BADGE_COLORS, LOGIC_NODE_GRADIENTS, LOGIC_NODE_HANDLE_COLORS, LangChainIcon, ListOperatorFlowNode, LogicNodeModal, MINIMAP_NODE_COLORS, ModelProviderFlowNode, NODE_EXECUTION_ACCENT_COLORS, NodeCard, NodeContextMenu, NoteFlowNode, OpenAIIcon, PanelContextMenu, ParameterExtractorFlowNode, QuestionClassifierFlowNode, RuleFlowNode, SelectionContextMenu, StartFlowNode, StrandsIcon, TemplateTransformFlowNode, ToolFlowNode, VariableAggregatorFlowNode, VariableAssignerFlowNode, WorkflowBuilderProvider, Workspace, getCompatibleModels, getDefaultFrameworkForModel, getEntityBadgeColor, getEntityGradient, getEntityHandleColor, getEntityIcon, getEntityMinimapColor, getFrameworkMeta, getNodeExecutionAccent, getNodeExecutionAccentRgb, isModelCompatibleWithFramework, useModalStore, useWorkflowBuilderClient, useWorkflowBuilderClientOptional, useWorkflowStore } from '../chunk-3VHSRL3N.mjs';
6
+ import { GlassModal, Button, FormInput, FormTextarea, FormSelect, FormGrid, Badge, ToggleSwitch, Input, DynamicIslandConfirm } from '../chunk-LLFU42KC.mjs';
6
7
  import { useTranslations } from '../chunk-7VJ7CMMT.mjs';
7
8
  import '../chunk-QWG2FMUN.mjs';
8
- import '../chunk-JB6RNAD2.mjs';
9
+ import '../chunk-D2JF6C3E.mjs';
9
10
  export { GraphNodeBadge, GraphNodeHeader, GraphNodeIconBubble, GraphNodeMeta } from '../chunk-OZNTQROP.mjs';
10
- export { topologicalSortAgents, validateGraphNodeConfigs, validateNodeConfig, validateWorkflowGraph } from '../chunk-53SRKVKQ.mjs';
11
11
  import { getAgentTier, createDefaultLogicNodeConfig } from '../chunk-WNCPAWLC.mjs';
12
12
  export { applyDagreLayout, createDefaultLogicNodeConfig, getAgentTier } from '../chunk-WNCPAWLC.mjs';
13
13
  import { memo, useCallback, useState, useEffect, useMemo, useRef, Children } from 'react';
@@ -437,8 +437,7 @@ function ConfigTab({ models, t, selectedModelId, setSelectedModelId, selectedFra
437
437
  ] })
438
438
  ] });
439
439
  }
440
- function PromptTab({ agent, temperature, setTemperature, markDirty, t }) {
441
- const [promptText, setPromptText] = useState(agent.systemPrompt ?? "");
440
+ function PromptTab({ agent, temperature, setTemperature, markDirty, t, promptText, setPromptText }) {
442
441
  const handleTemperatureChange = useCallback((event) => {
443
442
  setTemperature(parseFloat(event.target.value));
444
443
  markDirty();
@@ -712,7 +711,145 @@ function ToolsTab({ agentTools, enabledToolIds, onToggle, agentFramework, t }) {
712
711
  })
713
712
  ] });
714
713
  }
715
- function AgentModal({ onSaved }) {
714
+ function AdvancedTab({
715
+ displayName,
716
+ setDisplayName,
717
+ description,
718
+ setDescription,
719
+ maxOutputTokens,
720
+ setMaxOutputTokens,
721
+ topP,
722
+ setTopP,
723
+ topK,
724
+ setTopK,
725
+ tags,
726
+ setTags,
727
+ status,
728
+ setStatus,
729
+ avatarUrl,
730
+ setAvatarUrl,
731
+ markDirty,
732
+ t
733
+ }) {
734
+ const wrap = "w-full rounded-lg border border-gray-200/50 bg-gray-50/50 px-3 py-2 text-sm text-gray-800 outline-none transition-colors focus:border-indigo-300/50 focus:ring-1 focus:ring-indigo-300/30 dark:border-white/10 dark:bg-white/5 dark:text-gray-200";
735
+ const labelCls = "mb-1 block text-xs font-medium text-gray-500 dark:text-gray-400";
736
+ const touch = () => markDirty();
737
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-4 p-4", children: [
738
+ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 gap-3 sm:grid-cols-2", children: [
739
+ /* @__PURE__ */ jsxs("label", { children: [
740
+ /* @__PURE__ */ jsx("span", { className: labelCls, children: t("agentDrawer.displayName", { _: "Display name" }) }),
741
+ /* @__PURE__ */ jsx("input", { className: wrap, value: displayName, onChange: (e) => {
742
+ setDisplayName(e.target.value);
743
+ touch();
744
+ } })
745
+ ] }),
746
+ /* @__PURE__ */ jsxs("label", { children: [
747
+ /* @__PURE__ */ jsx("span", { className: labelCls, children: t("agentDrawer.status", { _: "Status" }) }),
748
+ /* @__PURE__ */ jsxs("select", { className: wrap, value: status, onChange: (e) => {
749
+ setStatus(e.target.value);
750
+ touch();
751
+ }, children: [
752
+ /* @__PURE__ */ jsx("option", { value: "draft", children: "draft" }),
753
+ /* @__PURE__ */ jsx("option", { value: "active", children: "active" }),
754
+ /* @__PURE__ */ jsx("option", { value: "archived", children: "archived" })
755
+ ] })
756
+ ] })
757
+ ] }),
758
+ /* @__PURE__ */ jsxs("label", { className: "block", children: [
759
+ /* @__PURE__ */ jsx("span", { className: labelCls, children: t("agentDrawer.description", { _: "Description" }) }),
760
+ /* @__PURE__ */ jsx(
761
+ "textarea",
762
+ {
763
+ className: wrap,
764
+ rows: 3,
765
+ value: description,
766
+ onChange: (e) => {
767
+ setDescription(e.target.value);
768
+ touch();
769
+ },
770
+ placeholder: t("agentDrawer.descriptionPlaceholder", { _: "What this agent does, which tasks it's good at" })
771
+ }
772
+ )
773
+ ] }),
774
+ /* @__PURE__ */ jsxs("label", { className: "block", children: [
775
+ /* @__PURE__ */ jsx("span", { className: labelCls, children: t("agentDrawer.avatarUrl", { _: "Avatar URL" }) }),
776
+ /* @__PURE__ */ jsx("input", { className: wrap, value: avatarUrl, onChange: (e) => {
777
+ setAvatarUrl(e.target.value);
778
+ touch();
779
+ }, placeholder: "https://\u2026" })
780
+ ] }),
781
+ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 gap-3 sm:grid-cols-3", children: [
782
+ /* @__PURE__ */ jsxs("label", { children: [
783
+ /* @__PURE__ */ jsx("span", { className: labelCls, children: t("agentDrawer.maxOutputTokens", { _: "Max output tokens" }) }),
784
+ /* @__PURE__ */ jsx(
785
+ "input",
786
+ {
787
+ type: "number",
788
+ min: 1,
789
+ max: 32768,
790
+ className: wrap,
791
+ value: maxOutputTokens,
792
+ onChange: (e) => {
793
+ setMaxOutputTokens(Number(e.target.value) || 0);
794
+ touch();
795
+ }
796
+ }
797
+ )
798
+ ] }),
799
+ /* @__PURE__ */ jsxs("label", { children: [
800
+ /* @__PURE__ */ jsx("span", { className: labelCls, children: t("agentDrawer.topP", { _: "Top-p" }) }),
801
+ /* @__PURE__ */ jsx(
802
+ "input",
803
+ {
804
+ type: "number",
805
+ step: 0.05,
806
+ min: 0,
807
+ max: 1,
808
+ className: wrap,
809
+ value: topP,
810
+ onChange: (e) => {
811
+ setTopP(Number(e.target.value) || 0);
812
+ touch();
813
+ }
814
+ }
815
+ )
816
+ ] }),
817
+ /* @__PURE__ */ jsxs("label", { children: [
818
+ /* @__PURE__ */ jsx("span", { className: labelCls, children: t("agentDrawer.topK", { _: "Top-k" }) }),
819
+ /* @__PURE__ */ jsx(
820
+ "input",
821
+ {
822
+ type: "number",
823
+ min: 0,
824
+ max: 500,
825
+ className: wrap,
826
+ value: topK,
827
+ onChange: (e) => {
828
+ setTopK(Number(e.target.value) || 0);
829
+ touch();
830
+ }
831
+ }
832
+ )
833
+ ] })
834
+ ] }),
835
+ /* @__PURE__ */ jsxs("label", { className: "block", children: [
836
+ /* @__PURE__ */ jsx("span", { className: labelCls, children: t("agentDrawer.tags", { _: "Tags (comma-separated)" }) }),
837
+ /* @__PURE__ */ jsx(
838
+ "input",
839
+ {
840
+ className: wrap,
841
+ value: tags,
842
+ onChange: (e) => {
843
+ setTags(e.target.value);
844
+ touch();
845
+ },
846
+ placeholder: "pricing, research, internal"
847
+ }
848
+ )
849
+ ] })
850
+ ] });
851
+ }
852
+ function AgentModal({ onSaved, onPersist }) {
716
853
  const t = useTranslations("agents.workflow");
717
854
  const activeModal = useModalStore((s) => s.activeModal);
718
855
  const agentData = useModalStore((s) => s.agentData);
@@ -730,6 +867,16 @@ function AgentModal({ onSaved }) {
730
867
  const [saved, setSaved] = useState(true);
731
868
  const [enabledToolIds, setEnabledToolIds] = useState(/* @__PURE__ */ new Set());
732
869
  const [selectedProviderId, setSelectedProviderId] = useState("");
870
+ const [systemPrompt, setSystemPrompt] = useState("");
871
+ const [displayName, setDisplayName] = useState("");
872
+ const [description, setDescription] = useState("");
873
+ const [avatarUrl, setAvatarUrlState] = useState("");
874
+ const [maxOutputTokens, setMaxOutputTokens] = useState(1024);
875
+ const [topP, setTopP] = useState(0.95);
876
+ const [topK, setTopK] = useState(40);
877
+ const [tagsText, setTagsText] = useState("");
878
+ const [status, setStatus] = useState("active");
879
+ const [persisting, setPersisting] = useState(false);
733
880
  const availableModelProviders = agentData?.modelProviders ?? [];
734
881
  const agentId = agent?.agentId ?? agent?.id ?? "";
735
882
  useEffect(() => {
@@ -743,6 +890,22 @@ function AgentModal({ onSaved }) {
743
890
  const agentToolIds = agent.agentToolIds;
744
891
  setEnabledToolIds(new Set(agentToolIds ?? availableAgentTools.filter((t2) => t2.enabled).map((t2) => t2.agentToolId)));
745
892
  setSelectedProviderId(agent.modelProviderId ?? "");
893
+ const agentRecord = agent;
894
+ setSystemPrompt(
895
+ agentRecord.systemPrompt ?? agentRecord.instruction ?? ""
896
+ );
897
+ setDisplayName(agentRecord.displayName ?? agent.name ?? "");
898
+ setDescription(agentRecord.description ?? "");
899
+ setAvatarUrlState(agentRecord.avatar ?? "");
900
+ const agentMaxOut = agentRecord.maxOutputTokens ?? agent.maxTokens ?? 1024;
901
+ setMaxOutputTokens(Number(agentMaxOut) || 1024);
902
+ setTopP(Number(agentRecord.topP ?? 0.95));
903
+ setTopK(Number(agentRecord.topK ?? 40));
904
+ const existingTags = agentRecord.tags;
905
+ setTagsText(
906
+ Array.isArray(existingTags) ? existingTags.filter((v) => typeof v === "string").join(", ") : ""
907
+ );
908
+ setStatus(agentRecord.status ?? "active");
746
909
  }, [agentId]);
747
910
  const dirty = !saved;
748
911
  const markDirty = useCallback(() => setSaved(false), []);
@@ -763,10 +926,58 @@ function AgentModal({ onSaved }) {
763
926
  });
764
927
  markDirty();
765
928
  }, [markDirty]);
766
- const handleMarkSaved = useCallback(() => {
767
- markSaved();
768
- onSaved?.();
769
- }, [markSaved, onSaved]);
929
+ const handleMarkSaved = useCallback(async () => {
930
+ if (!agent) return;
931
+ const payload = {
932
+ agentId: agent.agentId,
933
+ name: agent.name,
934
+ displayName: displayName.trim() || void 0,
935
+ description: description.trim() || void 0,
936
+ avatar: avatarUrl.trim() || void 0,
937
+ modelId: selectedModelId || void 0,
938
+ connectionId: selectedProviderId || void 0,
939
+ framework: selectedFramework,
940
+ systemPrompt,
941
+ temperature,
942
+ maxOutputTokens,
943
+ topP,
944
+ topK,
945
+ elo,
946
+ status: status || void 0,
947
+ enabledToolIds: Array.from(enabledToolIds),
948
+ tags: tagsText.split(",").map((tag) => tag.trim()).filter((tag) => tag.length > 0)
949
+ };
950
+ try {
951
+ setPersisting(true);
952
+ if (onPersist) await onPersist(payload);
953
+ markSaved();
954
+ onSaved?.();
955
+ } catch (error) {
956
+ console.error("[AgentModal] onPersist failed", error);
957
+ } finally {
958
+ setPersisting(false);
959
+ }
960
+ }, [
961
+ agent,
962
+ displayName,
963
+ description,
964
+ avatarUrl,
965
+ selectedModelId,
966
+ selectedProviderId,
967
+ selectedFramework,
968
+ systemPrompt,
969
+ temperature,
970
+ maxOutputTokens,
971
+ topP,
972
+ topK,
973
+ elo,
974
+ status,
975
+ enabledToolIds,
976
+ tagsText,
977
+ markSaved,
978
+ onSaved,
979
+ onPersist
980
+ ]);
770
981
  if (!agent) return null;
771
982
  const sections = [
772
983
  { id: "profile", label: t("agentDrawer.profileSection"), icon: UserCircleIcon, group: t("agentDrawer.agentGroup") },
@@ -774,19 +985,22 @@ function AgentModal({ onSaved }) {
774
985
  { id: "prompt", label: t("agentDrawer.promptSection"), icon: SparklesIcon, group: t("agentDrawer.agentGroup") },
775
986
  { id: "tools", label: `${t("agentDrawer.toolsTab")}${enabledToolIds.size > 0 ? ` (${enabledToolIds.size})` : ""}`, icon: CommandLineIcon, group: t("agentDrawer.configGroup") },
776
987
  { id: "models", label: `${t("agentDrawer.modelsTab")}${selectedProviderId ? " \u2713" : ""}`, icon: KeyIcon, group: t("agentDrawer.configGroup") },
988
+ { id: "advanced", label: t("agentDrawer.advancedTab", { _: "Advanced" }), icon: Cog6ToothIcon, group: t("agentDrawer.configGroup") },
777
989
  { id: "results", label: t("agentDrawer.resultsTab"), icon: PlayCircleIcon, group: t("agentDrawer.executionGroup") }
778
990
  ];
779
- const avatarUrl = agent.avatar;
991
+ const effectiveAvatarUrl = avatarUrl || agent.avatar;
780
992
  const sidebarFooter = /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 px-4 py-3", children: [
781
993
  /* @__PURE__ */ jsx("div", { role: "status", "aria-live": "polite", className: "flex-1 text-xs", children: dirty ? /* @__PURE__ */ jsx("span", { className: "text-amber-500 dark:text-amber-400", children: t("agentDrawer.unsavedChanges") }) : /* @__PURE__ */ jsx("span", { className: "text-emerald-500 dark:text-emerald-400", children: t("agentDrawer.saved") }) }),
782
994
  /* @__PURE__ */ jsx(
783
995
  "button",
784
996
  {
785
997
  type: "button",
786
- onClick: handleMarkSaved,
787
- disabled: !dirty,
788
- className: `rounded-lg px-3 py-1.5 text-xs font-semibold text-white shadow-sm transition-all ${dirty ? "bg-gradient-to-r from-indigo-500 to-purple-500 hover:from-indigo-600 hover:to-purple-600" : "cursor-not-allowed bg-gray-300 dark:bg-gray-700"}`,
789
- children: t("agentDrawer.save")
998
+ onClick: () => {
999
+ void handleMarkSaved();
1000
+ },
1001
+ disabled: !dirty || persisting,
1002
+ className: `rounded-lg px-3 py-1.5 text-xs font-semibold text-white shadow-sm transition-all ${dirty && !persisting ? "bg-gradient-to-r from-indigo-500 to-purple-500 hover:from-indigo-600 hover:to-purple-600" : "cursor-not-allowed bg-gray-300 dark:bg-gray-700"}`,
1003
+ children: persisting ? "\u2026" : t("agentDrawer.save")
790
1004
  }
791
1005
  )
792
1006
  ] });
@@ -805,9 +1019,9 @@ function AgentModal({ onSaved }) {
805
1019
  activeSectionId: activeTab,
806
1020
  onSectionChange: (sectionId) => setActiveTab(sectionId),
807
1021
  identity: {
808
- displayName: agent.name,
809
- profileInitial: agent.name.charAt(0).toUpperCase(),
810
- avatarUrl,
1022
+ displayName: displayName || agent.name,
1023
+ profileInitial: (displayName || agent.name).charAt(0).toUpperCase(),
1024
+ avatarUrl: effectiveAvatarUrl,
811
1025
  role: agent.role
812
1026
  },
813
1027
  footer: sidebarFooter
@@ -815,7 +1029,41 @@ function AgentModal({ onSaved }) {
815
1029
  children: [
816
1030
  activeTab === "profile" && /* @__PURE__ */ jsx(AgentProfileHeader, { agent, models, t, selectedModelId, setSelectedModelId, selectedFramework, temperature, setTemperature, elo, setElo, onChanged: markDirty }),
817
1031
  activeTab === "framework" && /* @__PURE__ */ jsx(ConfigTab, { models, t, selectedModelId, setSelectedModelId, selectedFramework, setSelectedFramework: (fw) => setSelectedFramework(fw), markDirty, connectedProviderTypes: availableModelProviders.map((p) => p.provider) }),
818
- activeTab === "prompt" && /* @__PURE__ */ jsx(PromptTab, { agent, temperature, setTemperature, markDirty, t }),
1032
+ activeTab === "prompt" && /* @__PURE__ */ jsx(
1033
+ PromptTab,
1034
+ {
1035
+ agent,
1036
+ temperature,
1037
+ setTemperature,
1038
+ markDirty,
1039
+ t,
1040
+ promptText: systemPrompt,
1041
+ setPromptText: setSystemPrompt
1042
+ }
1043
+ ),
1044
+ activeTab === "advanced" && /* @__PURE__ */ jsx(
1045
+ AdvancedTab,
1046
+ {
1047
+ displayName,
1048
+ setDisplayName,
1049
+ description,
1050
+ setDescription,
1051
+ maxOutputTokens,
1052
+ setMaxOutputTokens,
1053
+ topP,
1054
+ setTopP,
1055
+ topK,
1056
+ setTopK,
1057
+ tags: tagsText,
1058
+ setTags: setTagsText,
1059
+ status,
1060
+ setStatus,
1061
+ avatarUrl,
1062
+ setAvatarUrl: setAvatarUrlState,
1063
+ markDirty,
1064
+ t
1065
+ }
1066
+ ),
819
1067
  activeTab === "tools" && /* @__PURE__ */ jsx(ToolsTab, { agentTools: availableAgentTools, enabledToolIds, onToggle: handleToggleTool, agentFramework: selectedFramework, t }),
820
1068
  activeTab === "models" && /* @__PURE__ */ jsx(
821
1069
  ModelsTab,
@@ -1231,6 +1479,655 @@ function PipelineSettingsModal({ onSave }) {
1231
1479
  }
1232
1480
  );
1233
1481
  }
1482
+
1483
+ // src/astrlabe/components/rules/types.ts
1484
+ var RULE_STATUS_OPTIONS = ["draft", "active", "archived"];
1485
+ var TIMEZONE_OPTIONS = [
1486
+ "UTC",
1487
+ "America/Sao_Paulo",
1488
+ "America/New_York",
1489
+ "America/Los_Angeles",
1490
+ "Europe/London",
1491
+ "Europe/Lisbon"
1492
+ ];
1493
+ var OPERATOR_OPTIONS = [
1494
+ { value: "truthy", label: "Field is set (truthy)" },
1495
+ { value: "eq", label: "Equals" },
1496
+ { value: "neq", label: "Not equal to" },
1497
+ { value: "gt", label: "Greater than" },
1498
+ { value: "gte", label: "Greater than or equal" },
1499
+ { value: "lt", label: "Less than" },
1500
+ { value: "lte", label: "Less than or equal" },
1501
+ { value: "contains", label: "Contains (string)" },
1502
+ { value: "threshold", label: "Threshold" },
1503
+ { value: "regex_match", label: "Regex match" },
1504
+ { value: "time_window", label: "Time window" },
1505
+ { value: "boolean_expression", label: "And/Or group" }
1506
+ ];
1507
+ var SIMPLE_COMPARISON_OPERATORS = [
1508
+ "truthy",
1509
+ "eq",
1510
+ "neq",
1511
+ "gt",
1512
+ "gte",
1513
+ "lt",
1514
+ "lte",
1515
+ "contains"
1516
+ ];
1517
+ function RuleConditionBuilder({ value, onChange, depth = 0 }) {
1518
+ const handleOperatorChange = (op) => {
1519
+ const base = { operator: op };
1520
+ switch (op) {
1521
+ case "truthy":
1522
+ onChange({ ...base, field: value.field ?? "" });
1523
+ break;
1524
+ case "eq":
1525
+ case "neq":
1526
+ case "gt":
1527
+ case "gte":
1528
+ case "lt":
1529
+ case "lte":
1530
+ case "contains":
1531
+ onChange({ ...base, field: value.field ?? "", value: value.value ?? "" });
1532
+ break;
1533
+ case "threshold":
1534
+ onChange({
1535
+ ...base,
1536
+ field: value.field ?? "",
1537
+ comparison: value.comparison ?? "gte",
1538
+ value: typeof value.value === "number" ? value.value : 0
1539
+ });
1540
+ break;
1541
+ case "regex_match":
1542
+ onChange({ ...base, field: value.field ?? "", pattern: value.pattern ?? "" });
1543
+ break;
1544
+ case "time_window":
1545
+ onChange({
1546
+ ...base,
1547
+ field: value.field ?? "",
1548
+ timezone: value.timezone ?? "UTC",
1549
+ windows: value.windows && value.windows.length > 0 ? value.windows : [{ startHour: 6, endHour: 9 }]
1550
+ });
1551
+ break;
1552
+ case "boolean_expression":
1553
+ onChange({
1554
+ ...base,
1555
+ combinator: value.combinator ?? "and",
1556
+ operands: value.operands && value.operands.length > 0 ? value.operands : [{ operator: "gt", field: "", value: 0 }, { operator: "gt", field: "", value: 0 }]
1557
+ });
1558
+ break;
1559
+ }
1560
+ };
1561
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-3 rounded-xl border border-slate-200 bg-slate-50/60 p-3 dark:border-slate-700 dark:bg-slate-900/40", children: [
1562
+ /* @__PURE__ */ jsx(
1563
+ FormSelect,
1564
+ {
1565
+ label: "Operator",
1566
+ value: value.operator,
1567
+ options: OPERATOR_OPTIONS,
1568
+ onValueChange: (v) => handleOperatorChange(v)
1569
+ }
1570
+ ),
1571
+ SIMPLE_COMPARISON_OPERATORS.includes(value.operator) && /* @__PURE__ */ jsx(SimpleEditor, { value, onChange }),
1572
+ value.operator === "threshold" && /* @__PURE__ */ jsx(ThresholdEditor, { value, onChange }),
1573
+ value.operator === "regex_match" && /* @__PURE__ */ jsx(RegexEditor, { value, onChange }),
1574
+ value.operator === "time_window" && /* @__PURE__ */ jsx(TimeWindowEditor, { value, onChange }),
1575
+ value.operator === "boolean_expression" && depth < 1 && /* @__PURE__ */ jsx(BooleanGroupEditor, { value, onChange, depth: depth + 1 }),
1576
+ value.operator === "boolean_expression" && depth >= 1 && /* @__PURE__ */ jsx("p", { className: "text-xs italic text-slate-500", children: "Nested boolean groups are supported by the engine but not in this builder \u2014 edit the JSON directly for deeper trees." })
1577
+ ] });
1578
+ }
1579
+ function SimpleEditor({
1580
+ value,
1581
+ onChange
1582
+ }) {
1583
+ const isTruthy = value.operator === "truthy";
1584
+ return /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 gap-3 sm:grid-cols-2", children: [
1585
+ /* @__PURE__ */ jsx(
1586
+ FormInput,
1587
+ {
1588
+ label: "Field",
1589
+ hint: "Dotted reference into the variable pool \u2014 e.g. `parse-pricing.margin`, `inputs.fuelType`",
1590
+ value: value.field ?? "",
1591
+ onValueChange: (field) => onChange({ ...value, field }),
1592
+ placeholder: "node.path"
1593
+ }
1594
+ ),
1595
+ !isTruthy && /* @__PURE__ */ jsx(
1596
+ FormInput,
1597
+ {
1598
+ label: "Value",
1599
+ hint: "Literal. Numeric operators coerce via `as f64`.",
1600
+ value: value.value === null || value.value === void 0 ? "" : String(value.value),
1601
+ onValueChange: (raw) => onChange({ ...value, value: coerceScalar(raw) })
1602
+ }
1603
+ )
1604
+ ] });
1605
+ }
1606
+ function ThresholdEditor({
1607
+ value,
1608
+ onChange
1609
+ }) {
1610
+ return /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 gap-3 sm:grid-cols-3", children: [
1611
+ /* @__PURE__ */ jsx(
1612
+ FormInput,
1613
+ {
1614
+ label: "Field",
1615
+ value: value.field ?? "",
1616
+ onValueChange: (field) => onChange({ ...value, field }),
1617
+ placeholder: "parse-pricing.margin"
1618
+ }
1619
+ ),
1620
+ /* @__PURE__ */ jsx(
1621
+ FormSelect,
1622
+ {
1623
+ label: "Direction",
1624
+ value: value.comparison ?? "gte",
1625
+ options: [
1626
+ { value: "gt", label: "Greater than (>)" },
1627
+ { value: "gte", label: "At least (\u2265)" },
1628
+ { value: "lt", label: "Less than (<)" },
1629
+ { value: "lte", label: "At most (\u2264)" }
1630
+ ],
1631
+ onValueChange: (comparison) => onChange({ ...value, comparison })
1632
+ }
1633
+ ),
1634
+ /* @__PURE__ */ jsx(
1635
+ FormInput,
1636
+ {
1637
+ label: "Threshold",
1638
+ type: "number",
1639
+ value: value.value === null || value.value === void 0 ? "" : String(value.value),
1640
+ onValueChange: (raw) => onChange({ ...value, value: Number(raw) }),
1641
+ placeholder: "0.08"
1642
+ }
1643
+ )
1644
+ ] });
1645
+ }
1646
+ function RegexEditor({
1647
+ value,
1648
+ onChange
1649
+ }) {
1650
+ return /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 gap-3 sm:grid-cols-2", children: [
1651
+ /* @__PURE__ */ jsx(
1652
+ FormInput,
1653
+ {
1654
+ label: "Field",
1655
+ value: value.field ?? "",
1656
+ onValueChange: (field) => onChange({ ...value, field }),
1657
+ placeholder: "agent.text"
1658
+ }
1659
+ ),
1660
+ /* @__PURE__ */ jsx(
1661
+ FormInput,
1662
+ {
1663
+ label: "Pattern",
1664
+ hint: "Rust regex syntax \u2014 `(?i)` etc. supported.",
1665
+ value: value.pattern ?? "",
1666
+ onValueChange: (pattern) => onChange({ ...value, pattern }),
1667
+ placeholder: "diesel-s10"
1668
+ }
1669
+ )
1670
+ ] });
1671
+ }
1672
+ function TimeWindowEditor({
1673
+ value,
1674
+ onChange
1675
+ }) {
1676
+ const windows = value.windows ?? [];
1677
+ const update = (index, patch) => {
1678
+ const next = windows.map((win, i) => i === index ? { ...win, ...patch } : win);
1679
+ onChange({ ...value, windows: next });
1680
+ };
1681
+ const remove = (index) => {
1682
+ const next = windows.filter((_, i) => i !== index);
1683
+ onChange({ ...value, windows: next });
1684
+ };
1685
+ const add = () => onChange({ ...value, windows: [...windows, { startHour: 8, endHour: 17 }] });
1686
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
1687
+ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 gap-3 sm:grid-cols-2", children: [
1688
+ /* @__PURE__ */ jsx(
1689
+ FormSelect,
1690
+ {
1691
+ label: "Timezone",
1692
+ value: value.timezone ?? "UTC",
1693
+ options: TIMEZONE_OPTIONS.map((tz) => ({ value: tz, label: tz })),
1694
+ onValueChange: (timezone) => onChange({ ...value, timezone })
1695
+ }
1696
+ ),
1697
+ /* @__PURE__ */ jsx(
1698
+ FormInput,
1699
+ {
1700
+ label: "Timestamp field (optional)",
1701
+ hint: "Pulls a timestamp from the pool. Leave blank to use the run's wall-clock.",
1702
+ value: value.field ?? "",
1703
+ onValueChange: (field) => onChange({ ...value, field }),
1704
+ placeholder: "clock.ts"
1705
+ }
1706
+ )
1707
+ ] }),
1708
+ /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
1709
+ windows.map((win, index) => /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-[1fr_1fr_auto] items-end gap-2 rounded-lg border border-slate-200 p-2 dark:border-slate-700", children: [
1710
+ /* @__PURE__ */ jsx(
1711
+ FormInput,
1712
+ {
1713
+ label: `Start (hour)`,
1714
+ type: "number",
1715
+ min: 0,
1716
+ max: 23,
1717
+ value: String(win.startHour),
1718
+ onValueChange: (raw) => update(index, { startHour: clampHour(raw) })
1719
+ }
1720
+ ),
1721
+ /* @__PURE__ */ jsx(
1722
+ FormInput,
1723
+ {
1724
+ label: `End (hour)`,
1725
+ type: "number",
1726
+ min: 0,
1727
+ max: 24,
1728
+ value: String(win.endHour),
1729
+ onValueChange: (raw) => update(index, { endHour: clampHour(raw, 24) })
1730
+ }
1731
+ ),
1732
+ /* @__PURE__ */ jsx(Button, { type: "button", plain: true, onClick: () => remove(index), children: "Remove" })
1733
+ ] }, index)),
1734
+ /* @__PURE__ */ jsx(Button, { type: "button", outline: true, onClick: add, children: "+ Add window" })
1735
+ ] })
1736
+ ] });
1737
+ }
1738
+ function BooleanGroupEditor({
1739
+ value,
1740
+ onChange,
1741
+ depth
1742
+ }) {
1743
+ const operands = value.operands ?? [];
1744
+ const update = (index, next) => {
1745
+ const arr = operands.map((op, i) => i === index ? next : op);
1746
+ onChange({ ...value, operands: arr });
1747
+ };
1748
+ const remove = (index) => {
1749
+ onChange({ ...value, operands: operands.filter((_, i) => i !== index) });
1750
+ };
1751
+ const add = () => onChange({
1752
+ ...value,
1753
+ operands: [...operands, { operator: "gt", field: "", value: 0 }]
1754
+ });
1755
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
1756
+ /* @__PURE__ */ jsx(
1757
+ FormSelect,
1758
+ {
1759
+ label: "Combinator",
1760
+ value: value.combinator ?? "and",
1761
+ options: [{ value: "and", label: "All of (AND)" }, { value: "or", label: "Any of (OR)" }],
1762
+ onValueChange: (combinator) => onChange({ ...value, combinator })
1763
+ }
1764
+ ),
1765
+ /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
1766
+ operands.map((child, index) => /* @__PURE__ */ jsxs("div", { className: "relative rounded-lg border border-dashed border-slate-300 p-2 dark:border-slate-700", children: [
1767
+ /* @__PURE__ */ jsx(
1768
+ RuleConditionBuilder,
1769
+ {
1770
+ value: child,
1771
+ onChange: (next) => update(index, next),
1772
+ depth
1773
+ }
1774
+ ),
1775
+ /* @__PURE__ */ jsx("div", { className: "mt-2 text-right", children: /* @__PURE__ */ jsx(Button, { type: "button", plain: true, onClick: () => remove(index), children: "Remove operand" }) })
1776
+ ] }, index)),
1777
+ /* @__PURE__ */ jsx(Button, { type: "button", outline: true, onClick: add, children: "+ Add operand" })
1778
+ ] })
1779
+ ] });
1780
+ }
1781
+ function coerceScalar(raw) {
1782
+ const trimmed = raw.trim();
1783
+ if (trimmed === "true") return true;
1784
+ if (trimmed === "false") return false;
1785
+ if (trimmed !== "" && !Number.isNaN(Number(trimmed))) return Number(trimmed);
1786
+ return raw;
1787
+ }
1788
+ function clampHour(raw, max = 23) {
1789
+ const n = Number(raw);
1790
+ if (!Number.isFinite(n)) return 0;
1791
+ return Math.max(0, Math.min(max, Math.trunc(n)));
1792
+ }
1793
+ function defaultRuleCondition() {
1794
+ return { operator: "truthy", field: "" };
1795
+ }
1796
+ var ACTION_TYPE_OPTIONS = [
1797
+ { value: "adjust_price", label: "Adjust price (multiplier)" },
1798
+ { value: "enforce_min_margin", label: "Enforce minimum margin" },
1799
+ { value: "realign_to_competitor", label: "Realign to competitor" },
1800
+ { value: "request_manager_approval", label: "Request manager approval" },
1801
+ { value: "round_to", label: "Round to step" },
1802
+ { value: "alert", label: "Emit alert" },
1803
+ { value: "custom", label: "Custom action" }
1804
+ ];
1805
+ function RuleActionBuilder({ value, onChange }) {
1806
+ const type = value.type || "adjust_price";
1807
+ const params = value.params ?? {};
1808
+ const setParam = (key, newValue) => {
1809
+ onChange({ ...value, type, params: { ...params, [key]: newValue } });
1810
+ };
1811
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-3 rounded-xl border border-slate-200 bg-slate-50/60 p-3 dark:border-slate-700 dark:bg-slate-900/40", children: [
1812
+ /* @__PURE__ */ jsx(
1813
+ FormSelect,
1814
+ {
1815
+ label: "Action type",
1816
+ value: type,
1817
+ options: ACTION_TYPE_OPTIONS,
1818
+ onValueChange: (t) => onChange({ type: t, params: {} })
1819
+ }
1820
+ ),
1821
+ type === "adjust_price" && /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 gap-3 sm:grid-cols-2", children: [
1822
+ /* @__PURE__ */ jsx(
1823
+ FormInput,
1824
+ {
1825
+ label: "Multiplier",
1826
+ type: "number",
1827
+ step: 0.01,
1828
+ value: numberParam(params.multiplier, 1),
1829
+ onValueChange: (raw) => setParam("multiplier", Number(raw))
1830
+ }
1831
+ ),
1832
+ /* @__PURE__ */ jsx(
1833
+ FormInput,
1834
+ {
1835
+ label: "Reason code",
1836
+ value: stringParam(params.reason),
1837
+ onValueChange: (raw) => setParam("reason", raw),
1838
+ placeholder: "peak_demand_window"
1839
+ }
1840
+ )
1841
+ ] }),
1842
+ type === "enforce_min_margin" && /* @__PURE__ */ jsx(
1843
+ FormInput,
1844
+ {
1845
+ label: "Floor (fraction, e.g. 0.08)",
1846
+ type: "number",
1847
+ step: 0.01,
1848
+ value: numberParam(params.floor, 0.08),
1849
+ onValueChange: (raw) => setParam("floor", Number(raw))
1850
+ }
1851
+ ),
1852
+ type === "realign_to_competitor" && /* @__PURE__ */ jsx(
1853
+ FormInput,
1854
+ {
1855
+ label: "Tolerance (fraction)",
1856
+ type: "number",
1857
+ step: 0.01,
1858
+ value: numberParam(params.tolerance, 0.03),
1859
+ onValueChange: (raw) => setParam("tolerance", Number(raw))
1860
+ }
1861
+ ),
1862
+ type === "request_manager_approval" && /* @__PURE__ */ jsx(
1863
+ FormInput,
1864
+ {
1865
+ label: "Approval limit (fraction)",
1866
+ type: "number",
1867
+ step: 0.01,
1868
+ value: numberParam(params.limit, 0.05),
1869
+ onValueChange: (raw) => setParam("limit", Number(raw))
1870
+ }
1871
+ ),
1872
+ type === "round_to" && /* @__PURE__ */ jsx(
1873
+ FormInput,
1874
+ {
1875
+ label: "Step (e.g. 0.009)",
1876
+ type: "number",
1877
+ step: 1e-3,
1878
+ value: numberParam(params.step, 9e-3),
1879
+ onValueChange: (raw) => setParam("step", Number(raw))
1880
+ }
1881
+ ),
1882
+ type === "alert" && /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 gap-3 sm:grid-cols-2", children: [
1883
+ /* @__PURE__ */ jsx(
1884
+ FormInput,
1885
+ {
1886
+ label: "Channel",
1887
+ value: stringParam(params.channel),
1888
+ onValueChange: (raw) => setParam("channel", raw),
1889
+ placeholder: "slack"
1890
+ }
1891
+ ),
1892
+ /* @__PURE__ */ jsx(
1893
+ FormInput,
1894
+ {
1895
+ label: "Severity",
1896
+ value: stringParam(params.severity),
1897
+ onValueChange: (raw) => setParam("severity", raw),
1898
+ placeholder: "warning"
1899
+ }
1900
+ )
1901
+ ] }),
1902
+ type === "custom" && /* @__PURE__ */ jsx(CustomParamsEditor, { params, onChange: (p) => onChange({ ...value, type, params: p }) })
1903
+ ] });
1904
+ }
1905
+ function CustomParamsEditor({
1906
+ params,
1907
+ onChange
1908
+ }) {
1909
+ const entries = Object.entries(params);
1910
+ const update = (index, key, raw) => {
1911
+ const next = {};
1912
+ entries.forEach(([k, v], i) => {
1913
+ if (i === index) next[key] = coerce(raw);
1914
+ else next[k] = v;
1915
+ });
1916
+ onChange(next);
1917
+ };
1918
+ const remove = (index) => {
1919
+ const next = {};
1920
+ entries.forEach(([k, v], i) => {
1921
+ if (i !== index) next[k] = v;
1922
+ });
1923
+ onChange(next);
1924
+ };
1925
+ const add = () => {
1926
+ onChange({ ...params, [""]: "" });
1927
+ };
1928
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
1929
+ entries.map(([key, value], index) => /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-[1fr_1fr_auto] items-end gap-2", children: [
1930
+ /* @__PURE__ */ jsx(
1931
+ FormInput,
1932
+ {
1933
+ label: "Key",
1934
+ value: key,
1935
+ onValueChange: (raw) => update(index, raw, String(value ?? ""))
1936
+ }
1937
+ ),
1938
+ /* @__PURE__ */ jsx(
1939
+ FormInput,
1940
+ {
1941
+ label: "Value",
1942
+ value: String(value ?? ""),
1943
+ onValueChange: (raw) => update(index, key, raw)
1944
+ }
1945
+ ),
1946
+ /* @__PURE__ */ jsx(Button, { type: "button", plain: true, onClick: () => remove(index), children: "Remove" })
1947
+ ] }, index)),
1948
+ /* @__PURE__ */ jsx(Button, { type: "button", outline: true, onClick: add, children: "+ Add param" })
1949
+ ] });
1950
+ }
1951
+ function numberParam(value, fallback) {
1952
+ if (typeof value === "number" && Number.isFinite(value)) return String(value);
1953
+ return String(fallback);
1954
+ }
1955
+ function stringParam(value) {
1956
+ if (typeof value === "string") return value;
1957
+ if (value === null || value === void 0) return "";
1958
+ return String(value);
1959
+ }
1960
+ function coerce(raw) {
1961
+ const trimmed = raw.trim();
1962
+ if (trimmed === "true") return true;
1963
+ if (trimmed === "false") return false;
1964
+ if (trimmed !== "" && !Number.isNaN(Number(trimmed))) return Number(trimmed);
1965
+ return raw;
1966
+ }
1967
+ function defaultRuleAction() {
1968
+ return { type: "adjust_price", params: { multiplier: 1, reason: "" } };
1969
+ }
1970
+ function RuleForm({ value, onChange }) {
1971
+ const [showAdvanced, setShowAdvanced] = useState(
1972
+ Boolean(
1973
+ value.validFrom || value.validUntil || value.tags && value.tags.length > 0 || value.status
1974
+ )
1975
+ );
1976
+ const update = (key, v) => onChange({ ...value, [key]: v });
1977
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-5", children: [
1978
+ /* @__PURE__ */ jsxs(FormGrid, { children: [
1979
+ /* @__PURE__ */ jsx(
1980
+ FormInput,
1981
+ {
1982
+ label: "Name",
1983
+ required: true,
1984
+ value: value.name,
1985
+ onValueChange: (v) => update("name", v)
1986
+ }
1987
+ ),
1988
+ /* @__PURE__ */ jsx(
1989
+ FormInput,
1990
+ {
1991
+ label: "Priority",
1992
+ type: "number",
1993
+ min: 0,
1994
+ value: String(value.priority),
1995
+ onValueChange: (v) => update("priority", Number(v) || 0)
1996
+ }
1997
+ ),
1998
+ /* @__PURE__ */ jsx(
1999
+ FormSelect,
2000
+ {
2001
+ label: "Enabled",
2002
+ value: value.enabled ? "true" : "false",
2003
+ options: [
2004
+ { value: "true", label: "Enabled" },
2005
+ { value: "false", label: "Disabled" }
2006
+ ],
2007
+ onValueChange: (v) => update("enabled", v === "true")
2008
+ }
2009
+ ),
2010
+ /* @__PURE__ */ jsx(
2011
+ FormTextarea,
2012
+ {
2013
+ label: "Description",
2014
+ rows: 2,
2015
+ value: value.description ?? "",
2016
+ onValueChange: (v) => update("description", v)
2017
+ }
2018
+ )
2019
+ ] }),
2020
+ /* @__PURE__ */ jsxs("section", { children: [
2021
+ /* @__PURE__ */ jsxs("div", { className: "mb-2 flex items-center justify-between", children: [
2022
+ /* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold text-slate-700 dark:text-slate-200", children: "Condition" }),
2023
+ /* @__PURE__ */ jsx(Badge, { color: "indigo", children: value.condition.operator })
2024
+ ] }),
2025
+ /* @__PURE__ */ jsx(
2026
+ RuleConditionBuilder,
2027
+ {
2028
+ value: value.condition,
2029
+ onChange: (condition) => update("condition", condition)
2030
+ }
2031
+ )
2032
+ ] }),
2033
+ /* @__PURE__ */ jsxs("section", { children: [
2034
+ /* @__PURE__ */ jsxs("div", { className: "mb-2 flex items-center justify-between", children: [
2035
+ /* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold text-slate-700 dark:text-slate-200", children: "Action on match" }),
2036
+ /* @__PURE__ */ jsx(Badge, { color: "fuchsia", children: value.action.type })
2037
+ ] }),
2038
+ /* @__PURE__ */ jsx(
2039
+ RuleActionBuilder,
2040
+ {
2041
+ value: value.action,
2042
+ onChange: (action) => update("action", action)
2043
+ }
2044
+ )
2045
+ ] }),
2046
+ /* @__PURE__ */ jsxs("section", { children: [
2047
+ /* @__PURE__ */ jsx(
2048
+ "button",
2049
+ {
2050
+ type: "button",
2051
+ onClick: () => setShowAdvanced((s) => !s),
2052
+ className: "text-xs font-medium text-indigo-600 hover:text-indigo-500 dark:text-indigo-400",
2053
+ children: showAdvanced ? "\u2212 Hide schedule & tagging" : "+ Schedule, tagging, lifecycle"
2054
+ }
2055
+ ),
2056
+ showAdvanced && /* @__PURE__ */ jsxs("div", { className: "mt-3 grid grid-cols-1 gap-3 sm:grid-cols-2", children: [
2057
+ /* @__PURE__ */ jsx(
2058
+ FormInput,
2059
+ {
2060
+ label: "Valid from (ISO 8601)",
2061
+ type: "datetime-local",
2062
+ value: toDatetimeLocal(value.validFrom),
2063
+ onValueChange: (v) => update("validFrom", fromDatetimeLocal(v))
2064
+ }
2065
+ ),
2066
+ /* @__PURE__ */ jsx(
2067
+ FormInput,
2068
+ {
2069
+ label: "Valid until (ISO 8601)",
2070
+ type: "datetime-local",
2071
+ value: toDatetimeLocal(value.validUntil),
2072
+ onValueChange: (v) => update("validUntil", fromDatetimeLocal(v))
2073
+ }
2074
+ ),
2075
+ /* @__PURE__ */ jsx(
2076
+ FormSelect,
2077
+ {
2078
+ label: "Status",
2079
+ value: value.status ?? "active",
2080
+ options: RULE_STATUS_OPTIONS.map((s) => ({ value: s, label: s })),
2081
+ onValueChange: (v) => update("status", v)
2082
+ }
2083
+ ),
2084
+ /* @__PURE__ */ jsx(
2085
+ FormInput,
2086
+ {
2087
+ label: "Tags (comma-separated)",
2088
+ value: (value.tags ?? []).join(", "),
2089
+ onValueChange: (v) => update("tags", parseTags(v)),
2090
+ placeholder: "pricing, peak-hours"
2091
+ }
2092
+ )
2093
+ ] })
2094
+ ] })
2095
+ ] });
2096
+ }
2097
+ function defaultRuleForm() {
2098
+ return {
2099
+ name: "",
2100
+ description: "",
2101
+ enabled: true,
2102
+ priority: 0,
2103
+ status: "active",
2104
+ validFrom: null,
2105
+ validUntil: null,
2106
+ tags: [],
2107
+ condition: defaultRuleCondition(),
2108
+ action: defaultRuleAction()
2109
+ };
2110
+ }
2111
+ function parseTags(raw) {
2112
+ return raw.split(",").map((tag) => tag.trim()).filter((tag) => tag.length > 0);
2113
+ }
2114
+ function toDatetimeLocal(iso) {
2115
+ if (!iso) return "";
2116
+ try {
2117
+ const d = new Date(iso);
2118
+ if (Number.isNaN(d.getTime())) return "";
2119
+ const pad = (n) => String(n).padStart(2, "0");
2120
+ return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}T${pad(d.getHours())}:${pad(d.getMinutes())}`;
2121
+ } catch {
2122
+ return "";
2123
+ }
2124
+ }
2125
+ function fromDatetimeLocal(raw) {
2126
+ if (!raw) return null;
2127
+ const d = new Date(raw);
2128
+ if (Number.isNaN(d.getTime())) return null;
2129
+ return d.toISOString();
2130
+ }
1234
2131
  var DATASOURCE_LOGOS = {
1235
2132
  bigquery: "/logos/datasources/bigquery.svg",
1236
2133
  postgres: "/logos/datasources/postgres.svg",
@@ -3341,6 +4238,6 @@ function useHelpLines() {
3341
4238
  };
3342
4239
  }
3343
4240
 
3344
- export { AgentModal, AmazonNovaIcon, AnthropicModelIcon, AutoSaveWorkspace, DslExportModal, DslImportModal, DynamicIslandConfirm2 as DynamicIslandConfirm, MetaLlamaIcon, NodePalette, PipelineSettingsModal, PreviewPanel, RunInputDialog, RunPanel, SaveStatusBadge, SubworkflowModal, VariableInspector, VersionHistoryPanel, WorkflowListBar, getModelIcon, useCanRedo, useCanUndo, useCanvasShortcuts, useClipboard, useContextMenu, useEditingNodeId, useHasCopied, useHelpLines, useIsRunning, useNodeResults, useSelectedNodeCount, useSubworkflowStore, useUndoRedo };
4241
+ export { AgentModal, AmazonNovaIcon, AnthropicModelIcon, AutoSaveWorkspace, DslExportModal, DslImportModal, DynamicIslandConfirm2 as DynamicIslandConfirm, MetaLlamaIcon, NodePalette, PipelineSettingsModal, PreviewPanel, RULE_STATUS_OPTIONS, RuleActionBuilder, RuleConditionBuilder, RuleForm, RunInputDialog, RunPanel, SaveStatusBadge, SubworkflowModal, TIMEZONE_OPTIONS, VariableInspector, VersionHistoryPanel, WorkflowListBar, defaultRuleAction, defaultRuleCondition, defaultRuleForm, getModelIcon, useCanRedo, useCanUndo, useCanvasShortcuts, useClipboard, useContextMenu, useEditingNodeId, useHasCopied, useHelpLines, useIsRunning, useNodeResults, useSelectedNodeCount, useSubworkflowStore, useUndoRedo };
3345
4242
  //# sourceMappingURL=index.mjs.map
3346
4243
  //# sourceMappingURL=index.mjs.map