mirror of
https://github.com/Significant-Gravitas/AutoGPT.git
synced 2026-04-08 03:00:28 -04:00
merge EditTemplateModal into RunAgentModal
This commit is contained in:
@@ -69,7 +69,6 @@ export function NewAgentLibraryView() {
|
||||
</Button>
|
||||
}
|
||||
agent={agent}
|
||||
agentId={agent.id.toString()}
|
||||
onRunCreated={(execution) => handleSelectRun(execution.id)}
|
||||
onScheduleCreated={(schedule) =>
|
||||
handleSelectRun(`schedule:${schedule.id}`)
|
||||
|
||||
@@ -1,267 +0,0 @@
|
||||
"use client";
|
||||
|
||||
import { useState, useEffect } from "react";
|
||||
import { LibraryAgent } from "@/app/api/__generated__/models/libraryAgent";
|
||||
import { LibraryAgentPreset } from "@/app/api/__generated__/models/libraryAgentPreset";
|
||||
import { usePatchV2UpdateAnExistingPreset } from "@/app/api/__generated__/endpoints/presets/presets";
|
||||
|
||||
import { Button } from "@/components/atoms/Button/Button";
|
||||
import { Input } from "@/components/atoms/Input/Input";
|
||||
import { Dialog } from "@/components/molecules/Dialog/Dialog";
|
||||
import { useToast } from "@/components/molecules/Toast/use-toast";
|
||||
|
||||
import { AgentCostSection } from "./RunAgentModal/components/AgentCostSection/AgentCostSection";
|
||||
import { AgentSectionHeader } from "./RunAgentModal/components/AgentSectionHeader/AgentSectionHeader";
|
||||
import { AgentDetails } from "./RunAgentModal/components/AgentDetails/AgentDetails";
|
||||
import { ModalHeader } from "./RunAgentModal/components/ModalHeader/ModalHeader";
|
||||
import { ModalRunSection } from "./RunAgentModal/components/ModalRunSection/ModalRunSection";
|
||||
import { RunAgentModalContextProvider } from "./RunAgentModal/context";
|
||||
|
||||
interface Props {
|
||||
triggerSlot: React.ReactNode;
|
||||
agent: LibraryAgent;
|
||||
preset: LibraryAgentPreset;
|
||||
onSaved?: (updatedPreset: LibraryAgentPreset) => void;
|
||||
}
|
||||
|
||||
export function EditTemplateModal({
|
||||
triggerSlot,
|
||||
agent,
|
||||
preset,
|
||||
onSaved,
|
||||
}: Props) {
|
||||
const { toast } = useToast();
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const [presetName, setPresetName] = useState(preset.name);
|
||||
const [presetDescription, setPresetDescription] = useState(
|
||||
preset.description,
|
||||
);
|
||||
const [inputValues, setInputValues] = useState<Record<string, any>>(
|
||||
preset.inputs || {},
|
||||
);
|
||||
const [inputCredentials, setInputCredentials] = useState<Record<string, any>>(
|
||||
preset.credentials || {},
|
||||
);
|
||||
|
||||
// Reset form when preset changes
|
||||
useEffect(() => {
|
||||
setPresetName(preset.name);
|
||||
setPresetDescription(preset.description);
|
||||
setInputValues(preset.inputs || {});
|
||||
setInputCredentials(preset.credentials || {});
|
||||
}, [preset]);
|
||||
|
||||
// Update preset mutation
|
||||
const updatePresetMutation = usePatchV2UpdateAnExistingPreset({
|
||||
mutation: {
|
||||
onSuccess: (response) => {
|
||||
if (response.status === 200) {
|
||||
toast({
|
||||
title: "Template updated successfully",
|
||||
variant: "default",
|
||||
});
|
||||
setIsOpen(false);
|
||||
onSaved?.(response.data as unknown as LibraryAgentPreset);
|
||||
}
|
||||
},
|
||||
onError: (error: any) => {
|
||||
toast({
|
||||
title: "Failed to update template",
|
||||
description: error.message || "An unexpected error occurred.",
|
||||
variant: "destructive",
|
||||
});
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// Input schema validation (reusing logic from useAgentRunModal)
|
||||
const agentInputSchema = agent.trigger_setup_info
|
||||
? agent.trigger_setup_info.config_schema
|
||||
: agent.input_schema;
|
||||
const agentInputFields = (() => {
|
||||
if (
|
||||
!agentInputSchema ||
|
||||
typeof agentInputSchema !== "object" ||
|
||||
!("properties" in agentInputSchema) ||
|
||||
!agentInputSchema.properties
|
||||
) {
|
||||
return {};
|
||||
}
|
||||
const properties = agentInputSchema.properties as Record<string, any>;
|
||||
return Object.fromEntries(
|
||||
Object.entries(properties).filter(
|
||||
([_, subSchema]: [string, any]) => !subSchema.hidden,
|
||||
),
|
||||
);
|
||||
})();
|
||||
|
||||
const agentCredentialsInputFields = (() => {
|
||||
if (
|
||||
!agent.credentials_input_schema ||
|
||||
typeof agent.credentials_input_schema !== "object" ||
|
||||
!("properties" in agent.credentials_input_schema) ||
|
||||
!agent.credentials_input_schema.properties
|
||||
) {
|
||||
return {} as Record<string, any>;
|
||||
}
|
||||
return agent.credentials_input_schema.properties as Record<string, any>;
|
||||
})();
|
||||
|
||||
const hasAnySetupFields =
|
||||
Object.keys(agentInputFields || {}).length > 0 ||
|
||||
Object.keys(agentCredentialsInputFields || {}).length > 0;
|
||||
|
||||
function handleInputChange(key: string, value: string) {
|
||||
setInputValues((prev) => ({
|
||||
...prev,
|
||||
[key]: value,
|
||||
}));
|
||||
}
|
||||
|
||||
function handleCredentialsChange(key: string, value: any | undefined) {
|
||||
setInputCredentials((prev) => {
|
||||
const next = { ...prev } as Record<string, any>;
|
||||
if (value === undefined) {
|
||||
delete next[key];
|
||||
return next;
|
||||
}
|
||||
next[key] = value;
|
||||
return next;
|
||||
});
|
||||
}
|
||||
|
||||
function handleSetOpen(open: boolean) {
|
||||
setIsOpen(open);
|
||||
}
|
||||
|
||||
function handleSave() {
|
||||
updatePresetMutation.mutate({
|
||||
presetId: preset.id,
|
||||
data: {
|
||||
name: presetName,
|
||||
description: presetDescription,
|
||||
inputs: inputValues,
|
||||
credentials: inputCredentials,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
const hasChanges =
|
||||
presetName !== preset.name ||
|
||||
presetDescription !== preset.description ||
|
||||
JSON.stringify(inputValues) !== JSON.stringify(preset.inputs || {}) ||
|
||||
JSON.stringify(inputCredentials) !==
|
||||
JSON.stringify(preset.credentials || {});
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
controlled={{ isOpen, set: handleSetOpen }}
|
||||
styling={{ maxWidth: "600px", maxHeight: "90vh" }}
|
||||
>
|
||||
<Dialog.Trigger>{triggerSlot}</Dialog.Trigger>
|
||||
<Dialog.Content>
|
||||
<div className="flex h-full flex-col pb-4">
|
||||
{/* Header */}
|
||||
<div className="flex-shrink-0">
|
||||
<ModalHeader agent={agent} />
|
||||
<AgentCostSection flowId={agent.graph_id} />
|
||||
</div>
|
||||
|
||||
{/* Scrollable content */}
|
||||
<div className="flex-1 pr-1" style={{ scrollbarGutter: "stable" }}>
|
||||
{/* Template Info Section */}
|
||||
<div className="mt-10">
|
||||
<AgentSectionHeader title="Template Information" />
|
||||
<div className="mb-10 mt-4 space-y-4">
|
||||
<div className="flex flex-col space-y-2">
|
||||
<label className="text-sm font-medium">Template Name</label>
|
||||
<Input
|
||||
id="template_name"
|
||||
label="Template Name"
|
||||
size="small"
|
||||
hideLabel
|
||||
value={presetName}
|
||||
placeholder="Enter template name"
|
||||
onChange={(e) => setPresetName(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-col space-y-2">
|
||||
<label className="text-sm font-medium">
|
||||
Template Description
|
||||
</label>
|
||||
<Input
|
||||
id="template_description"
|
||||
label="Template Description"
|
||||
size="small"
|
||||
hideLabel
|
||||
value={presetDescription}
|
||||
placeholder="Enter template description"
|
||||
onChange={(e) => setPresetDescription(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Setup Section */}
|
||||
{hasAnySetupFields ? (
|
||||
<div className="mt-8">
|
||||
<RunAgentModalContextProvider
|
||||
value={{
|
||||
agent,
|
||||
defaultRunType: "manual", // Always manual for templates
|
||||
presetName,
|
||||
setPresetName,
|
||||
presetDescription,
|
||||
setPresetDescription,
|
||||
inputValues,
|
||||
setInputValue: handleInputChange,
|
||||
agentInputFields,
|
||||
inputCredentials,
|
||||
setInputCredentialsValue: handleCredentialsChange,
|
||||
agentCredentialsInputFields,
|
||||
}}
|
||||
>
|
||||
<>
|
||||
<AgentSectionHeader title="Template Setup" />
|
||||
<ModalRunSection />
|
||||
</>
|
||||
</RunAgentModalContextProvider>
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
{/* Agent Details Section */}
|
||||
<div className="mt-8">
|
||||
<AgentSectionHeader title="Agent Details" />
|
||||
<AgentDetails agent={agent} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Dialog.Footer
|
||||
className="fixed bottom-1 left-0 z-10 w-full bg-white p-4"
|
||||
style={{ boxShadow: "0px -8px 10px white" }}
|
||||
>
|
||||
<div className="flex items-center justify-end gap-3">
|
||||
<Button
|
||||
variant="secondary"
|
||||
onClick={() => setIsOpen(false)}
|
||||
disabled={updatePresetMutation.isPending}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
variant="primary"
|
||||
onClick={handleSave}
|
||||
disabled={
|
||||
!hasChanges ||
|
||||
updatePresetMutation.isPending ||
|
||||
!presetName.trim()
|
||||
}
|
||||
>
|
||||
{updatePresetMutation.isPending ? "Saving..." : "Save Changes"}
|
||||
</Button>
|
||||
</div>
|
||||
</Dialog.Footer>
|
||||
</Dialog.Content>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import { GraphExecutionMeta } from "@/app/api/__generated__/models/graphExecutio
|
||||
import { LibraryAgent } from "@/app/api/__generated__/models/libraryAgent";
|
||||
import { LibraryAgentPreset } from "@/app/api/__generated__/models/libraryAgentPreset";
|
||||
import { Button } from "@/components/atoms/Button/Button";
|
||||
import { Input } from "@/components/atoms/Input/Input";
|
||||
import { Dialog } from "@/components/molecules/Dialog/Dialog";
|
||||
import { AlarmIcon } from "@phosphor-icons/react";
|
||||
import { useState } from "react";
|
||||
@@ -21,7 +22,6 @@ import { useAgentRunModal } from "./useAgentRunModal";
|
||||
interface Props {
|
||||
triggerSlot: React.ReactNode;
|
||||
agent: LibraryAgent;
|
||||
agentId: string;
|
||||
agentVersion?: number;
|
||||
initialInputValues?: Record<string, any>;
|
||||
initialInputCredentials?: Record<string, any>;
|
||||
@@ -30,6 +30,10 @@ interface Props {
|
||||
onRunCreated?: (execution: GraphExecutionMeta) => void;
|
||||
onTriggerSetup?: (preset: LibraryAgentPreset) => void;
|
||||
onScheduleCreated?: (schedule: GraphExecutionJobInfo) => void;
|
||||
editMode?: {
|
||||
preset: LibraryAgentPreset;
|
||||
onSaved?: (updatedPreset: LibraryAgentPreset) => void;
|
||||
};
|
||||
}
|
||||
|
||||
export function RunAgentModal({
|
||||
@@ -42,6 +46,7 @@ export function RunAgentModal({
|
||||
onRunCreated,
|
||||
onTriggerSetup,
|
||||
onScheduleCreated,
|
||||
editMode,
|
||||
}: Props) {
|
||||
const {
|
||||
// UI state
|
||||
@@ -65,6 +70,11 @@ export function RunAgentModal({
|
||||
setPresetName,
|
||||
setPresetDescription,
|
||||
|
||||
// Edit mode
|
||||
hasChanges,
|
||||
isUpdatingPreset,
|
||||
handleSave,
|
||||
|
||||
// Validation/readiness
|
||||
allRequiredInputsAreSet,
|
||||
|
||||
@@ -81,10 +91,12 @@ export function RunAgentModal({
|
||||
} = useAgentRunModal(agent, {
|
||||
onRun: onRunCreated,
|
||||
onSetupTrigger: onTriggerSetup,
|
||||
onScheduleCreated,
|
||||
initialInputValues,
|
||||
initialInputCredentials,
|
||||
initialPresetName,
|
||||
initialPresetDescription,
|
||||
editMode,
|
||||
});
|
||||
|
||||
const [isScheduleModalOpen, setIsScheduleModalOpen] = useState(false);
|
||||
@@ -130,6 +142,8 @@ export function RunAgentModal({
|
||||
onScheduleCreated?.(schedule);
|
||||
}
|
||||
|
||||
const templateOrTrigger = agent.trigger_setup_info ? "Trigger" : "Template";
|
||||
|
||||
return (
|
||||
<>
|
||||
<Dialog
|
||||
@@ -147,23 +161,64 @@ export function RunAgentModal({
|
||||
|
||||
{/* Scrollable content */}
|
||||
<div className="flex-1 pr-1" style={{ scrollbarGutter: "stable" }}>
|
||||
{/* Template Info Section (Edit Mode Only) */}
|
||||
{editMode && (
|
||||
<div className="mt-10">
|
||||
<AgentSectionHeader
|
||||
title={`${templateOrTrigger} Information`}
|
||||
/>
|
||||
<div className="mb-10 mt-4 space-y-4">
|
||||
<div className="flex flex-col space-y-2">
|
||||
<label className="text-sm font-medium">
|
||||
{templateOrTrigger} Name
|
||||
</label>
|
||||
<Input
|
||||
id="template_name"
|
||||
label="Template Name"
|
||||
size="small"
|
||||
hideLabel
|
||||
value={presetName}
|
||||
placeholder="Enter template name"
|
||||
onChange={(e) => setPresetName(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-col space-y-2">
|
||||
<label className="text-sm font-medium">
|
||||
{templateOrTrigger} Description
|
||||
</label>
|
||||
<Input
|
||||
id="template_description"
|
||||
label="Template Description"
|
||||
size="small"
|
||||
hideLabel
|
||||
value={presetDescription}
|
||||
placeholder="Enter template description"
|
||||
onChange={(e) => setPresetDescription(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{/* Setup Section */}
|
||||
<div className="mt-10">
|
||||
<div className={editMode ? "mt-8" : "mt-10"}>
|
||||
{hasAnySetupFields ? (
|
||||
<RunAgentModalContextProvider
|
||||
value={{
|
||||
agent,
|
||||
defaultRunType,
|
||||
presetName,
|
||||
setPresetName,
|
||||
presetDescription,
|
||||
setPresetDescription,
|
||||
inputValues,
|
||||
setInputValue: handleInputChange,
|
||||
agentInputFields,
|
||||
inputCredentials,
|
||||
setInputCredentialsValue: handleCredentialsChange,
|
||||
agentCredentialsInputFields,
|
||||
presetEditMode: Boolean(
|
||||
editMode || agent.trigger_setup_info,
|
||||
),
|
||||
presetName,
|
||||
setPresetName,
|
||||
presetDescription,
|
||||
setPresetDescription,
|
||||
}}
|
||||
>
|
||||
<>
|
||||
@@ -192,27 +247,51 @@ export function RunAgentModal({
|
||||
style={{ boxShadow: "0px -8px 10px white" }}
|
||||
>
|
||||
<div className="flex items-center justify-end gap-3">
|
||||
{(defaultRunType == "manual" || defaultRunType == "schedule") && (
|
||||
<Button
|
||||
variant="secondary"
|
||||
onClick={handleOpenScheduleModal}
|
||||
disabled={
|
||||
isExecuting ||
|
||||
isSettingUpTrigger ||
|
||||
!allRequiredInputsAreSet
|
||||
}
|
||||
>
|
||||
<AlarmIcon size={16} />
|
||||
Schedule Agent
|
||||
</Button>
|
||||
{editMode ? (
|
||||
<>
|
||||
<Button
|
||||
variant="secondary"
|
||||
onClick={() => setIsOpen(false)}
|
||||
disabled={isUpdatingPreset}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
variant="primary"
|
||||
onClick={handleSave}
|
||||
disabled={
|
||||
!hasChanges || isUpdatingPreset || !presetName.trim()
|
||||
}
|
||||
>
|
||||
{isUpdatingPreset ? "Saving..." : "Save Changes"}
|
||||
</Button>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
{(defaultRunType == "manual" ||
|
||||
defaultRunType == "schedule") && (
|
||||
<Button
|
||||
variant="secondary"
|
||||
onClick={handleOpenScheduleModal}
|
||||
disabled={
|
||||
isExecuting ||
|
||||
isSettingUpTrigger ||
|
||||
!allRequiredInputsAreSet
|
||||
}
|
||||
>
|
||||
<AlarmIcon size={16} />
|
||||
Schedule Agent
|
||||
</Button>
|
||||
)}
|
||||
<RunActions
|
||||
defaultRunType={defaultRunType}
|
||||
onRun={handleRun}
|
||||
isExecuting={isExecuting}
|
||||
isSettingUpTrigger={isSettingUpTrigger}
|
||||
isRunReady={allRequiredInputsAreSet}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
<RunActions
|
||||
defaultRunType={defaultRunType}
|
||||
onRun={handleRun}
|
||||
isExecuting={isExecuting}
|
||||
isSettingUpTrigger={isSettingUpTrigger}
|
||||
isRunReady={allRequiredInputsAreSet}
|
||||
/>
|
||||
</div>
|
||||
{(defaultRunType == "manual" || defaultRunType == "schedule") && (
|
||||
<ScheduleAgentModal
|
||||
|
||||
@@ -13,16 +13,17 @@ export function ModalRunSection() {
|
||||
const {
|
||||
agent,
|
||||
defaultRunType,
|
||||
presetName,
|
||||
setPresetName,
|
||||
presetDescription,
|
||||
setPresetDescription,
|
||||
inputValues,
|
||||
setInputValue,
|
||||
agentInputFields,
|
||||
inputCredentials,
|
||||
setInputCredentialsValue,
|
||||
agentCredentialsInputFields,
|
||||
presetEditMode,
|
||||
presetName,
|
||||
setPresetName,
|
||||
presetDescription,
|
||||
setPresetDescription,
|
||||
} = useRunAgentModalContext();
|
||||
|
||||
return (
|
||||
@@ -30,7 +31,7 @@ export function ModalRunSection() {
|
||||
{defaultRunType === "automatic-trigger" && <WebhookTriggerBanner />}
|
||||
|
||||
{/* Preset/Trigger fields */}
|
||||
{defaultRunType === "automatic-trigger" && (
|
||||
{presetEditMode && (
|
||||
<div className="flex flex-col gap-4">
|
||||
<div className="flex flex-col space-y-2">
|
||||
<label className="flex items-center gap-1 text-sm font-medium">
|
||||
|
||||
@@ -4,14 +4,9 @@ import React, { createContext, useContext } from "react";
|
||||
import { LibraryAgent } from "@/app/api/__generated__/models/libraryAgent";
|
||||
import { RunVariant } from "./useAgentRunModal";
|
||||
|
||||
export interface RunAgentModalContextValue {
|
||||
export type RunAgentModalContextValue = {
|
||||
agent: LibraryAgent;
|
||||
defaultRunType: RunVariant;
|
||||
// Preset / Trigger
|
||||
presetName: string;
|
||||
setPresetName: (value: string) => void;
|
||||
presetDescription: string;
|
||||
setPresetDescription: (value: string) => void;
|
||||
// Inputs
|
||||
inputValues: Record<string, any>;
|
||||
setInputValue: (key: string, value: any) => void;
|
||||
@@ -20,7 +15,14 @@ export interface RunAgentModalContextValue {
|
||||
inputCredentials: Record<string, any>;
|
||||
setInputCredentialsValue: (key: string, value: any | undefined) => void;
|
||||
agentCredentialsInputFields: Record<string, any>;
|
||||
}
|
||||
|
||||
// Trigger / Preset fields
|
||||
presetEditMode: boolean; // determines whether to show the name and description fields
|
||||
presetName: string;
|
||||
setPresetName: (value: string) => void;
|
||||
presetDescription: string;
|
||||
setPresetDescription: (value: string) => void;
|
||||
};
|
||||
|
||||
const RunAgentModalContext = createContext<RunAgentModalContextValue | null>(
|
||||
null,
|
||||
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
import {
|
||||
getGetV2ListPresetsQueryKey,
|
||||
usePostV2SetupTrigger,
|
||||
usePatchV2UpdateAnExistingPreset,
|
||||
} from "@/app/api/__generated__/endpoints/presets/presets";
|
||||
import { GraphExecutionMeta } from "@/app/api/__generated__/models/graphExecutionMeta";
|
||||
import { GraphExecutionJobInfo } from "@/app/api/__generated__/models/graphExecutionJobInfo";
|
||||
@@ -31,11 +32,15 @@ export type RunVariant =
|
||||
interface UseAgentRunModalCallbacks {
|
||||
onRun?: (execution: GraphExecutionMeta) => void;
|
||||
onSetupTrigger?: (preset: LibraryAgentPreset) => void;
|
||||
onCreateSchedule?: (schedule: GraphExecutionJobInfo) => void;
|
||||
onScheduleCreated?: (schedule: GraphExecutionJobInfo) => void;
|
||||
initialInputValues?: Record<string, any>;
|
||||
initialInputCredentials?: Record<string, any>;
|
||||
initialPresetName?: string;
|
||||
initialPresetDescription?: string;
|
||||
editMode?: {
|
||||
preset: LibraryAgentPreset;
|
||||
onSaved?: (updatedPreset: LibraryAgentPreset) => void;
|
||||
};
|
||||
}
|
||||
|
||||
export function useAgentRunModal(
|
||||
@@ -47,16 +52,20 @@ export function useAgentRunModal(
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const [showScheduleView, setShowScheduleView] = useState(false);
|
||||
const [inputValues, setInputValues] = useState<Record<string, any>>(
|
||||
callbacks?.initialInputValues || {},
|
||||
callbacks?.initialInputValues || callbacks?.editMode?.preset?.inputs || {},
|
||||
);
|
||||
const [inputCredentials, setInputCredentials] = useState<Record<string, any>>(
|
||||
callbacks?.initialInputCredentials || {},
|
||||
callbacks?.initialInputCredentials ||
|
||||
callbacks?.editMode?.preset?.credentials ||
|
||||
{},
|
||||
);
|
||||
const [presetName, setPresetName] = useState<string>(
|
||||
callbacks?.initialPresetName || "",
|
||||
callbacks?.initialPresetName || callbacks?.editMode?.preset?.name || "",
|
||||
);
|
||||
const [presetDescription, setPresetDescription] = useState<string>(
|
||||
callbacks?.initialPresetDescription || "",
|
||||
callbacks?.initialPresetDescription ||
|
||||
callbacks?.editMode?.preset?.description ||
|
||||
"",
|
||||
);
|
||||
const defaultScheduleName = useMemo(() => `Run ${agent.name}`, [agent.name]);
|
||||
const [scheduleName, setScheduleName] = useState(defaultScheduleName);
|
||||
@@ -116,7 +125,7 @@ export function useAgentRunModal(
|
||||
toast({
|
||||
title: "Schedule created",
|
||||
});
|
||||
callbacks?.onCreateSchedule?.(response.data);
|
||||
callbacks?.onScheduleCreated?.(response.data);
|
||||
// Invalidate schedules list for this graph
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: getGetV1ListExecutionSchedulesForAGraphQueryKey(
|
||||
@@ -172,11 +181,36 @@ export function useAgentRunModal(
|
||||
},
|
||||
});
|
||||
|
||||
// Input schema validation
|
||||
const agentInputSchema = useMemo(
|
||||
() => agent.input_schema || { properties: {}, required: [] },
|
||||
[agent.input_schema],
|
||||
);
|
||||
// Edit mode mutation for updating presets
|
||||
const updatePresetMutation = usePatchV2UpdateAnExistingPreset({
|
||||
mutation: {
|
||||
onSuccess: (response) => {
|
||||
if (response.status === 200) {
|
||||
toast({
|
||||
title: "Template updated successfully",
|
||||
variant: "default",
|
||||
});
|
||||
setIsOpen(false);
|
||||
callbacks?.editMode?.onSaved?.(response.data);
|
||||
}
|
||||
},
|
||||
onError: (error: any) => {
|
||||
toast({
|
||||
title: "Failed to update template",
|
||||
description: error.message || "An unexpected error occurred.",
|
||||
variant: "destructive",
|
||||
});
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// Input schema validation (use trigger schema for triggered agents)
|
||||
const agentInputSchema = useMemo(() => {
|
||||
if (agent.trigger_setup_info?.config_schema) {
|
||||
return agent.trigger_setup_info.config_schema;
|
||||
}
|
||||
return agent.input_schema || { properties: {}, required: [] };
|
||||
}, [agent.input_schema, agent.trigger_setup_info]);
|
||||
|
||||
const agentInputFields = useMemo(() => {
|
||||
if (
|
||||
@@ -367,6 +401,8 @@ export function useAgentRunModal(
|
||||
createScheduleMutation,
|
||||
toast,
|
||||
userTimezone,
|
||||
presetName,
|
||||
completeOnboardingStep,
|
||||
]);
|
||||
|
||||
function handleShowSchedule() {
|
||||
@@ -393,6 +429,47 @@ export function useAgentRunModal(
|
||||
setCronExpression(expression);
|
||||
}
|
||||
|
||||
// Edit mode save handler
|
||||
const handleSave = useCallback(() => {
|
||||
if (!callbacks?.editMode?.preset) return;
|
||||
|
||||
updatePresetMutation.mutate({
|
||||
presetId: callbacks.editMode.preset.id,
|
||||
data: {
|
||||
name: presetName,
|
||||
description: presetDescription,
|
||||
inputs: inputValues,
|
||||
credentials: inputCredentials,
|
||||
},
|
||||
});
|
||||
}, [
|
||||
callbacks?.editMode?.preset,
|
||||
presetName,
|
||||
presetDescription,
|
||||
inputValues,
|
||||
inputCredentials,
|
||||
updatePresetMutation,
|
||||
]);
|
||||
|
||||
// Check if there are changes in edit mode
|
||||
const hasChanges = useMemo(() => {
|
||||
if (!callbacks?.editMode?.preset) return false;
|
||||
const preset = callbacks.editMode.preset;
|
||||
return (
|
||||
presetName !== preset.name ||
|
||||
presetDescription !== preset.description ||
|
||||
JSON.stringify(inputValues) !== JSON.stringify(preset.inputs || {}) ||
|
||||
JSON.stringify(inputCredentials) !==
|
||||
JSON.stringify(preset.credentials || {})
|
||||
);
|
||||
}, [
|
||||
callbacks?.editMode?.preset,
|
||||
presetName,
|
||||
presetDescription,
|
||||
inputValues,
|
||||
inputCredentials,
|
||||
]);
|
||||
|
||||
const hasInputFields = useMemo(() => {
|
||||
return Object.keys(agentInputFields).length > 0;
|
||||
}, [agentInputFields]);
|
||||
@@ -437,6 +514,7 @@ export function useAgentRunModal(
|
||||
isExecuting: executeGraphMutation.isPending,
|
||||
isCreatingSchedule: createScheduleMutation.isPending,
|
||||
isSettingUpTrigger: setupTriggerMutation.isPending,
|
||||
isUpdatingPreset: updatePresetMutation.isPending,
|
||||
|
||||
// Actions
|
||||
handleRun,
|
||||
@@ -445,5 +523,7 @@ export function useAgentRunModal(
|
||||
handleGoBack,
|
||||
handleSetScheduleName,
|
||||
handleSetCronExpression,
|
||||
handleSave,
|
||||
hasChanges,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -46,7 +46,6 @@ export function EmptyAgentRuns({ agent }: Props) {
|
||||
</Button>
|
||||
}
|
||||
agent={agent}
|
||||
agentId={agent.id.toString()}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -27,7 +27,6 @@ import { GraphExecutionMeta } from "@/app/api/__generated__/models/graphExecutio
|
||||
import { RunDetailCard } from "../RunDetailCard/RunDetailCard";
|
||||
import { Skeleton } from "@/components/__legacy__/ui/skeleton";
|
||||
import { AgentInputsReadOnly } from "../../modals/AgentInputsReadOnly/AgentInputsReadOnly";
|
||||
import { EditTemplateModal } from "../../modals/EditTemplateModal";
|
||||
import { RunAgentModal } from "../../modals/RunAgentModal/RunAgentModal";
|
||||
import { okData } from "@/app/api/helpers";
|
||||
|
||||
@@ -192,7 +191,6 @@ export function SelectedTemplateView({
|
||||
</Button>
|
||||
}
|
||||
agent={agent}
|
||||
agentId={agent.id}
|
||||
initialInputValues={preset.inputs || {}}
|
||||
initialInputCredentials={preset.credentials || {}}
|
||||
initialPresetName={preset.name}
|
||||
@@ -201,7 +199,7 @@ export function SelectedTemplateView({
|
||||
onScheduleCreated={onCreateSchedule}
|
||||
/>
|
||||
) : null}
|
||||
<EditTemplateModal
|
||||
<RunAgentModal
|
||||
triggerSlot={
|
||||
<Button
|
||||
variant="secondary"
|
||||
@@ -212,8 +210,10 @@ export function SelectedTemplateView({
|
||||
</Button>
|
||||
}
|
||||
agent={agent}
|
||||
preset={preset}
|
||||
onSaved={onSave}
|
||||
editMode={{
|
||||
preset,
|
||||
onSaved: onSave,
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
// TODO: add confirmation modal before deleting
|
||||
|
||||
Reference in New Issue
Block a user